Округление до двух знаков после запятой в JavaScript: правильное решение
Правильное округление до двух знаков после запятой
Проблема, которую вы описали, действительно классическая. Она связана с тем, как числа с плавающей точкой хранятся в JavaScript (стандарт IEEE 754). Из-за этого прямое умножение и деление может давать неожиданные результаты.
Вот несколько надёжных решений, проверенных на практике:
Решение 1: Использование Number.EPSILON
Самый простой и популярный способ — добавить очень маленькое число (Number.EPSILON) перед округлением, чтобы компенсировать погрешности:
function roundToTwo(num) {
return Math.round((num + Number.EPSILON) * 100) / 100;
}
// Примеры
console.log(roundToTwo(1.005)); // 1.01 ✓
console.log(roundToTwo(1.255)); // 1.26 ✓
console.log(roundToTwo(10.5)); // 10.5 ✓
console.log(roundToTwo(0.1 + 0.2)); // 0.3 ✓
console.log(roundToTwo(2.345)); // 2.35 ✓
Решение 2: Универсальная функция с любым количеством знаков
Если нужно округлять не только до двух знаков, вот более гибкий вариант:
function roundToDecimal(num, decimals = 2) {
const multiplier = Math.pow(10, decimals);
return Math.round((num + Number.EPSILON) * multiplier) / multiplier;
}
// Использование
console.log(roundToDecimal(1.005, 2)); // 1.01
console.log(roundToDecimal(3.14159, 3)); // 3.142
console.log(roundToDecimal(0.1 + 0.2, 2)); // 0.3
Решение 3: Без Number.EPSILON (через toFixed с преобразованием)
Можно использовать toFixed, но затем преобразовать результат обратно в число:
function roundToTwoFixed(num) {
return Number(num.toFixed(2));
}
// Но помните про баг с 1.005
console.log(roundToTwoFixed(1.005)); // 1.00 — баг!
console.log(roundToTwoFixed(2.345)); // 2.35 — работает
Важно: toFixed сам по себе имеет баги с округлением (например, 1.005), поэтому первый способ с Number.EPSILON надёжнее.
Почему это работает?
- Number.EPSILON — это наименьшая разница между двумя представимыми числами с плавающей точкой (примерно 2.22e-16).
- Добавляя эту крошечную величину перед округлением, мы «подталкиваем» число вверх, компенсируя погрешности представления.
- Например,
1.005 * 100в JavaScript даёт100.49999999999999, а не100.5. ДобавлениеNumber.EPSILONисправляет это.
Рекомендации
- Всегда используйте
Number.EPSILONдля надёжного округления. - Если работаете с деньгами, лучше перевести всё в копейки/центы (целые числа) и работать с ними.
- Для критически точных расчётов (финансы, наука) используйте библиотеки вроде
decimal.jsилиbig.js.
Альтернативный подход: через строковую обработку
Если вам не нравится идея с Number.EPSILON или вы хотите полный контроль над округлением, можно использовать строковый метод:
function roundToTwo(num) {
// Преобразуем в строку с большим количеством знаков
const str = num.toString();
const decimalIndex = str.indexOf('.');
// Если нет десятичной части или она короче 2 знаков — возвращаем как есть
if (decimalIndex === -1 || str.length - decimalIndex - 1 <= 2) {
return num;
}
// Округляем через toFixed и преобразуем обратно
return Number(Number(num).toFixed(2));
}
Плюсы:
- Понятная логика — вы сами решаете, когда округлять.
- Можно легко адаптировать для разных правил округления (математическое, банковское).
Минусы:
- Медленнее, чем математические методы.
- Всё равно страдает от багов
toFixedдля некоторых чисел (например, 1.005).
Этот подход лучше использовать, когда вам нужно не просто округлить, а ещё и проверить, нужно ли это делать (например, для форматирования вывода). Для чисто математических операций всё же рекомендую первый вариант с Number.EPSILON.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги