Вычисление дробного числа в системе определяется особенностями исполнения языка JavaScript в браузере пользователя и в NodeJS на сервере.
В JavaScript число представлено в виде 64-битного формата IEEE-754, т. е. хранит значение с плавающей точкой, что может приводить к неточным вычислениям.
Например, следующее сравнение будет определено в JavaScript как ложное:
alert( 0.1 + 0.2 == 0.3 ); // false
Это связано с тем, что значение 0,3 в JavaScript вычисляется следующим образом:
alert( 0.1 + 0.2 ); // 0.30000000000000004
Происходит так потому, что число хранится в памяти как последовательность бит — единиц и нулей. И дроби в такой двоичной системе становятся бесконечной дробью.
В JavaScript не хранятся точные значения 0.1 или 0.2. Числовой формат IEEE-754 округляет такие дроби до ближайшего возможного числа, например:
alert( 0.1.toFixed(20) ); // 0.10000000000000000555
Когда мы суммируем две дроби, их «неточности» тоже суммируются.
Отметим, что ошибка в точности вычислений для чисел с плавающей точкой сохраняется в любом языке, где используется формат IEEE-754, включая PHP, Java, C, Perl, Ruby.
Как решить проблему вычисления дробей
Можно попробовать полностью отказаться от дробей или использовать следующие способы.
- Округлить результат вычисления с помощью метода
toFixed(n)
:
let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // 0.30
Метод toFixed(n)
всегда возвращает строку, поэтому вы получите результат с заданным количеством цифр в десятичной части.
- Временно умножить число на 100 (или большее), чтобы привести его к целому. Затем выполнить математические действия и разделить обратно на 100. Суммируя целые числа, вы уменьшаете погрешность, но она всё равно появляется при финальном делении:
alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001
Таким образом, метод умножения и деления уменьшает погрешность, но полностью не решает её.