Как округлить число в JavaScript — Math.round, toFixed и почему появляются хвосты вроде 0.30000000000000004?
Складываю 0.1 + 0.2, а получаю 0.30000000000000004 — откуда хвост? И как вообще округлить число до пары знаков после запятой по-нормальному?
2 ответа
Хвост — не баг 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 — просто отбросить дробь
Главные грабли:
-
toFixed возвращает строку, не число. Если дальше считать — оберни в
Number(), иначе словишь склейку строк. -
Сравнивать дроби напрямую опасно из-за хвостов. Сравнивай с допуском:
Math.abs((0.1 + 0.2) - 0.3) < 1e-9; // true
- Деньги лучше считать в копейках/центах целыми числами, а делить на 100 только при выводе — так хвостов не будет вовсе:
const cents = 10 + 20; // 30 копеек
console.log((cents / 100).toFixed(2)); // '0.30'
Короткий рецепт «округлить до 2 знаков и получить число»:
const round2 = (n) => Math.round(n * 100) / 100;
console.log(round2(0.1 + 0.2)); // 0.3
Умножаем на 100, округляем до целого, делим обратно. В отличие от toFixed, сразу получаем число, а не строку. Для вывода с гарантированными двумя знаками (например, цена 5.00) всё же toFixed(2).