Як правильно округлити число до потрібної кількості знаків після крапки JS

Думаю всі програмісти зіткалися з проблемою, коли потрібно округлити нескінченне число до потрібної кількості знаків після крапки. Тут є декілька варіантів, і ви можете обрати потрібний, виходячи з ваших потреб.

Ліпший варіант, це спочатку помножити число на кількість знаків, і після поділити:

const to8Ceil = (val) => {
    if (!val) return 0
    const num = parseFloat(val)
    return Math.ceil(num * 100000000) / 100000000
}

const nm = 0.123456789 
// to8Ceil(nm) -- 0.12345679

Цей код як бачите округляє до більшого значення, якщо ж вам потрібно округлити до меншого, то замість Math.ceil, ви можете використати Math.floor

Як зберігати точні данні?

Але звідси ви можете зіткнутися з проблемою, коли вам потрібно показати користувачу одне число, наприклад в полі, але це число може вираховувати інші точні данні, і в такому випадку обраховуючи округлене число, ви зіткнетеся з проблемою, що фінальний результат буде невірним.

Наприклад:

  • Вам треба порахувати вартість однієї валюти до іншої.
  • Курс 0.33123123123.
  • В першому полі, пускай USD - у вас 725
  • Ви рахуєте 0.33123123123 * 725 = 240.142642642
  • Припустимо вам треба округлити до 5 знаків to5Ceil
  • to5Ceil(240.142642642) = 240.14265

Наче все добре, у користувача в полі 2 буде невірне для обислення значення. Тому що 240.14265 / 0.33123123123 != 725, а буде дорівнювати 725.000022215. Це сталося через те, що ви округлене число приняли як істину, тому вам доведеться зберігати 2 варінти. Тобто замість змінної, ви зберігаєте обʼєкт, в якому у вас буде: точне значення, реактивне округлене значення (toCeil), та id за необхідністю.

const obj = {
  amount: 0.33213123123,
  id: "field_1"
  get ceilAmount() {
    return Math.ceil(this.amount * 1000) / 1000;
  }
}

console.log(obj.ceilAmount); // 0.333

Таким чином коли вам буде потрібно взаємодіяти з полями, ви використовуєте як значення поля обʼєкт з певним id, та його внутрішню змінну amount, як змінну для вираховування логіки. Але в саме значення поля ви виводите ceilAmount, що візуально виводить користувачу округлену сумму, але вираховування йде по точному значенню. 

Але тут може бути і інша проблема - якщо користувач захоче змінити поле 2, де зараз введено округлене число, то фактично воно стане істиним, і в поле 1 буде повернуте не корректне значення. Нажаль тут нема корректного рішення.

Ви звістно можете зберігати в обʼєкт більше данних, та вираховувати різницію коофіцієнту, але все одно, коли потіи користувач змінить поле 1, то вам доведеться попадати ще в більші граблі. Тому я би рекомендував залишити це як данність, тому що навіть криптокалькулятори всі, на це забивають... І все працює в одностороньому порядку.