Проблемы точности JavaScript и исследовать решения

внешний интерфейс Безопасность JavaScript

Прочитав эту статью, вы сможете понять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, библиотека преобразует десятичные числа в целые числа, а затем обрабатывает их;

Ссылки по теме

Категории