Прочитав эту статью, вы сможете понять0.1 + 0.2
почему равно0.30000000000000004
И откуда берется максимальное безопасное число в JavaScript.
Чтобы обеспечить актуальность и точность контента, вы можетеПосмотреть исходный текст
Как преобразовать десятичное число в двоичное
Возьмите 173,8125 в качестве примера того, как преобразовать его в двоичное число.
① Для целой части 173 возьмем除 2 取余,逆序排列
;
173 / 2 = 86 ... 1
86 / 2 = 43 ... 0
43 / 2 = 21 ... 1 ↑
21 / 2 = 10 ... 1 | 逆序排列
10 / 2 = 5 ... 0 |
5 / 2 = 2 ... 1 |
2 / 2 = 1 ... 0
1 / 2 = 0 ... 1
Двоичная часть целой части10101101
.
② Для дробной части 0,8125 используйте乘 2 取整,顺序排列
;
0.8125 * 2 = 1.625 |
0.625 * 2 = 1.25 | 顺序排列
0.25 * 2 = 0.5 |
0.5 * 2 = 1 ↓
Двоичная часть дробной части1101
.
③. Добавьте результаты предыдущих двух частей, результат10101101.1101
;
Будьте осторожны, двоичные десятичные числа теряют точность!
Опираясь на полученные знания, преобразуйте десятичную дробь0.1
Преобразовать в двоичный:
0.1 * 2 = 0.2
0.2 * 2 = 0.4 // 注意这里
0.4 * 2 = 0.8
0.8 * 2 = 1.6
0.6 * 2 = 1.2
0.2 * 2 = 0.4 // 注意这里,循环开始
0.4 * 2 = 0.8
0.8 * 2 = 1.6
0.6 * 2 = 1.2
...
Можно найти конечные десятичные дроби0.1
но преобразованы в бесконечные двоичные десятичные числа0.00011001100...
, вы можете видеть, что точность теряется в процессе преобразования!
Десятичное число, которое можно преобразовать в конечное двоичное число, должно заканчиваться цифрой 5 (поскольку только 0,5 * 2 может стать целым числом). Итак, один десятичный знак в десятичном0.1 ~ 0.9
за исключением0.5
Значения вне этого теряют точность при преобразовании в двоичные.
Выведите, почему 0,1 + 0,2 равно 0,30000000000000004
Все значения в JavaScript соответствуют стандарту IEEE-754.64 bit
Номер с плавающей точкой двойной точности сохраняется. Давайте сначала понять стандарт IEEE-754Номер с плавающей точкой двойной точности.
Эта картинка очень важна.На ней видно, что число двойной точности с плавающей запятой по стандарту IEEE-754 состоит из трех частей, а именно:
- знак (знак): занимает 1 бит, указывающий положительный или отрицательный;
- exponent (экспонента): занимает 11 бит, обозначая диапазон;
- мантисса (mantissa): занимает 52 бита, указывая на точность, если лишний конец равен 1, его нужно нести;
Рекомендуемое чтениеЛовушки с плавающей запятой в JavaScript и решения, прочитав эту статью, вы сможете понять происхождение следующей формулы.
Общее количество битов точности равно битам 53. Поскольку оно представлено экспоненциальной нотацией, первая фиксированная единица не занимает места. То есть 1 в (M + 1) в формуле. Кроме того, 1023 в формуле — это половина от 2^11. Те, которые меньше 1023, используются для представления десятичных дробей, а те, которые больше 1023, используются для представления целых чисел.
Показатель степени можно контролировать до 2 ^ 1024 - 1, а максимальная точность составляет всего 2 ^ 53 - 1. Сравнивая их, можно сделать вывод, что на самом деле очень мало чисел, которые JavaScript может точно представить.
0.1
Преобразуется в двоичный как0.0001100110011...
, выраженный в научной записи как1.100110011... x 2^(-4)
, согласно приведенной выше формуле,S
для0
(1 бит),E
для-4 + 1023
, соответствующий двоичный файл01111111011
(11 бит),M
для1001100110011001100110011001100110011001100110011010
(52 бит, также обратите внимание на перенос в конце),0.1
Схема хранения выглядит следующим образом:
По аналогии,0.2
Преобразуется в двоичный как0.001100110011...
, выраженный в научной записи как1.100110011... x 2^(-3)
, согласно приведенной выше формуле,E
для-3 + 1023
, соответствующий двоичный файл01111111100
, M
для1001100110011001100110011001100110011001100110011010
, 0.2
Схема хранения выглядит следующим образом:
0.1 + 0.2
то есть 2^(-4) x 1.1001100110011001100110011001100110011001100110011010 и 2^(-3) x 1.1001100110011001100110011001100110011001 и 10011001 и 10011001
// 计算过程
0.00011001100110011001100110011001100110011001100110011010
0.0011001100110011001100110011001100110011001100110011010
// 相加得
0.01001100110011001100110011001100110011001100110011001110
0.01001100110011001100110011001100110011001100110011001110
Преобразуется в десятичное число0.30000000000000004
. Проверка завершена!
Как появилось максимальное безопасное число в JavaScript
В соответствии с составом чисел с плавающей запятой двойной точности количество цифр точности равно53 bit
. Безопасный номер означает в-2^53 ~ 2^53
Целые числа внутри (исключая границы) соответствуют уникальным числам двойной точности с плавающей запятой. На примере лучше понять:
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
Math.pow(2, 53)
даже сMath.pow(2, 53) + 1
равный! Это связано с тем, что Math.pow(2, 53) + 1 превысил предел точности мантиссы (53 бита), в этом примереMath.pow(2, 53)
а такжеMath.pow(2, 53) + 1
Соответствует тому же числу двойной точности с плавающей запятой. такMath.pow(2, 53)
Это не безопасный номер.
Максимальное безопасное число
Math.pow(2, 53) - 1
,Прямо сейчас9007199254740991
.
Проблемы с точностью, возникающие в бизнесе, и решения
Как понимание проблем точности JavaScript помогает нашему бизнесу? Например, бизнес-сценарий: например, есть одноклассник по серверной части Java, который определяет тип long, но когда номер заказа преобразуется в тип JavaScript Number, точность будет потеряна. Без вышеуказанного знания, невозможно понять, почему теряется точность.
Решения примерно такие:
1. Для больших целых чисел рассмотрите возможность использования типа bigint (в настоящее время на этапе 3);
2. ИспользуйтеbigNumber, его идея состоит в том, чтобы преобразовать его в строку для обработки, что оказывает определенное влияние на производительность;
3. Рассмотрите возможность использованияlong.js, его идея состоит в том, чтобы преобразовать значение типа long в два значения типа double с точностью до 32 бит.
4. Может использоваться для десятичных знаковnumber-precision, библиотека преобразует десятичные числа в целые числа, а затем обрабатывает их;