← Все вопросы

Как округлить число в JavaScript — Math.round, toFixed и почему появляются хвосты вроде 0.30000000000000004?

Задан 11 месяцев назад430 просмотров2 ответа
10

Складываю 0.1 + 0.2, а получаю 0.30000000000000004 — откуда хвост? И как вообще округлить число до пары знаков после запятой по-нормальному?

2 ответа

14
✓ Принятый ответ — помог автору

Хвост — не баг JS, а особенность чисел с плавающей точкой (стандарт, общий для многих языков). Дробные 0.1 и 0.2 нельзя точно представить в двоичной системе, поэтому сумма чуть «промахивается».

console.log(0.1 + 0.2);        // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false (!)

Округление до N знаков — toFixed (возвращает строку):

const x = 0.1 + 0.2;
console.log(x.toFixed(2)); // '0.30' — строка!
console.log(Number(x.toFixed(2))); // 0.3 — снова число

Округление до целого — Math:

Math.round(4.5); // 5 — к ближайшему
Math.floor(4.9); // 4 — вниз
Math.ceil(4.1);  // 5 — вверх
Math.trunc(4.9); // 4 — просто отбросить дробь

Главные грабли:

  1. toFixed возвращает строку, не число. Если дальше считать — оберни в Number(), иначе словишь склейку строк.

  2. Сравнивать дроби напрямую опасно из-за хвостов. Сравнивай с допуском:

Math.abs((0.1 + 0.2) - 0.3) < 1e-9; // true
  1. Деньги лучше считать в копейках/центах целыми числами, а делить на 100 только при выводе — так хвостов не будет вовсе:
const cents = 10 + 20; // 30 копеек
console.log((cents / 100).toFixed(2)); // '0.30'
5

Короткий рецепт «округлить до 2 знаков и получить число»:

const round2 = (n) => Math.round(n * 100) / 100;
console.log(round2(0.1 + 0.2)); // 0.3

Умножаем на 100, округляем до целого, делим обратно. В отличие от toFixed, сразу получаем число, а не строку. Для вывода с гарантированными двумя знаками (например, цена 5.00) всё же toFixed(2).

Ваш ответ

Войдите, чтобы ответить на вопрос.