Числа и Math в JavaScript: округление и точность
Содержание
О проверке кода. Примеры не запускались — в отличие от материалов по Node.js с фактическим выводом. Говорим прямо, а не ставим бейдж «проверено».
Числа в JavaScript — источник знаменитых мемов вроде 0.1 + 0.2. За «странностями» стоит понятная логика. Разберём, как считать правильно.
Знаменитое 0.1 + 0.2
0.1 + 0.2; // 0.30000000000000004 ❌
0.1 + 0.2 === 0.3; // false
Это не баг JavaScript. Все числа хранятся в двоичном формате с плавающей точкой (стандарт IEEE 754, один тип number для всего). В двоичной системе 0.1 и 0.2 не представимы точно — как 1/3 не записать конечной десятичной дробью. Отсюда микроскопическая погрешность. То же самое в Python, Java, C — везде, где float.
Правильное сравнение дробей — через допуск:
Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON; // true — с учётом погрешности
Округление
Math.round(4.5); // 5 — к ближайшему целому
Math.round(4.4); // 4
Math.floor(4.9); // 4 — всегда вниз
Math.ceil(4.1); // 5 — всегда вверх
Math.trunc(4.9); // 4 — отбросить дробную часть
Math.trunc(-4.9); // -4 — (floor дал бы -5!)
(3.14159).toFixed(2); // '3.14' — ⚠️ СТРОКА, не число!
Math-методы → число
Возвращают number
round— к ближайшемуfloor— вниз (к -∞)ceil— вверх (к +∞)trunc— отбросить дробь (к нулю)
toFixed → строка
Осторожно с типом
(1.5).toFixed(2)→'1.50'- Результат — СТРОКА
- Для вывода/форматирования — ок
- Для расчётов сначала
Number(...)
Главная ловушка toFixed — он возвращает строку. (3.14159).toFixed(2) даёт '3.14', а не 3.14. Для отображения это нормально, но если результат идёт в дальнейшие расчёты — оберните в Number(). Разница floor и trunc видна на отрицательных: floor(-4.9) = -5, trunc(-4.9) = -4.
Парсинг строк в числа
Number('42'); // 42
Number('42px'); // NaN — строгий, весь текст должен быть числом
Number(''); // 0 — пустая строка → 0 (сюрприз)
Number(' 42 '); // 42 — пробелы обрезает
parseInt('42px'); // 42 — терпимый, читает с начала
parseInt('3.9'); // 3 — только целая часть
parseFloat('3.9kg'); // 3.9 — с дробной частью
Number строгий — при любом мусоре в строке даёт NaN. parseInt/parseFloat терпимые — читают число с начала и останавливаются на первом нечисловом символе. Для пользовательского ввода вроде '42px' берут parseInt; для строгой валидации — Number с проверкой на NaN.
NaN и проверки
NaN === NaN; // false ❌ — NaN не равен ДАЖЕ САМОМУ СЕБЕ
Number.isNaN(NaN); // true ✅ — надёжная проверка
Number.isNaN('текст'); // false — не приводит типы (в отличие от старого isNaN)
Number.isFinite(42); // true
Number.isFinite(Infinity); // false
Number.isInteger(3.0); // true
NaN — единственное значение, не равное самому себе, поэтому x === NaN бесполезно. Проверяют через Number.isNaN(x). Берите именно Number.isNaN, а не старый глобальный isNaN — тот приводит типы и даёт ложные срабатывания (isNaN('текст') → true). Number.isFinite отсекает NaN и Infinity разом.
Деньги: считайте в копейках
// ❌ дробные рубли — накопление ошибок
let total = 0;
total += 19.99;
total += 19.99; // может получиться 39.980000000000004
// ✅ копейки целыми числами
let cents = 0;
cents += 1999;
cents += 1999; // 3998 — точно
const rubles = (cents / 100).toFixed(2); // '39.98' только при выводе
Универсальное правило финансовых расчётов: храните суммы в наименьших единицах целыми числами — копейках, центах. Целочисленная арифметика точна, погрешность плавающей точки исключена. Деление на 100 — только в момент отображения. Так делают в банковских системах и платёжных API.
Форматирование и большие числа
(1234567.89).toLocaleString('ru-RU'); // '1 234 567,89' — с разделителями
Number.MAX_SAFE_INTEGER; // 9007199254740991 — предел точных целых
9007199254740991 + 2; // 9007199254740992 — ❌ потеря точности!
9007199254740991n + 2n; // 9007199254740993n — ✅ BigInt для огромных целых
toLocaleString форматирует число под локаль (разделители разрядов, валюта). Для целых чисел больше MAX_SAFE_INTEGER (≈9 квадриллионов) обычный number теряет точность — тогда используют BigInt (суффикс n), например для ID из базы или криптографии.
Смежные темы: типы данных — number среди прочих; операторы — арифметика и сравнение; строки — форматирование вывода. Полный список — в уроках JavaScript.