Посмотрите на числовой тип в JS от 0.1+0.2=0.30000000000000004

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

написать впереди

Когда я сегодня читал «Расширенное программирование на JavaScript», я заметил, что в книге конкретно упоминается0.1+0.2=0.30000000000000004Такая проблема с ошибкой вычисления числа с плавающей запятой, я думаю, это очень интересно. Обычно на работе я мало разбираюсь в числах с плавающей запятой.Так случилось, что недавно моя группа студентов также столкнулась с этой проблемой.Я собираюсь обобщить этот, казалось бы, простой базовый тип числа, но это не просто. Цель этого блога — изучить и обобщить соответствующие знания о числах с плавающей запятой из этого странного результата вычислений.

два установленных факта

  1. Диапазон абсолютных значений чисел, которые могут быть представлены в JS, составляет 5e-324 ~ 1,7976931348623157e+308, что может быть достигнуто с помощьюNumber.MAX_VALUEа такжеNumber.MIN_VALUEбыть подтвержденным
  2. Диапазон крупнейшего безопасного целого числа, который можно представить в JS: -9007199254740991 ~ 9007199254740991, который может быть достигнутNumber.MIN_SAFE_INTEGERа такжеNumber.MAX_SAFE_INTEGERпроверять

Две существующие проблемы

  1. Существует проблема потери точности в четырех арифметических операциях, таких как:01 + 0.2 //0.30000000000000004
  2. Операции, которые превышают наибольшее безопасное целое число, являются небезопасными, например:9007199254740991 + 2 // 9007199254740992

Почему?

Чтобы объяснить два вышеупомянутых факта и проблемы, вам нужно знать, как десятичные числа хранятся в компьютерах:

Точка знаний! ! !

  1. Преобразуйте это число с плавающей запятой в соответствующее двоичное число и представьте его в экспоненциальной записи.
  2. передать это значение черезIEEE 754Стандартное представление в виде значения, которое фактически будет храниться в компьютере.

Мы знаем, что тип Number в JS использует тип с плавающей запятой двойной точности, который является типом double в других языках. Число двойной точности с плавающей запятой хранится в битах 64. Структурная схема выглядит следующим образом:

То есть число типа Number будет представлено в памяти как:s x m x 2^eтакой формат.

существуетСпецификация ЭСДиапазон e указывается в -1074 ~ 971, а максимальное число, которое m может представлять, составляет 52 1, а минимальный может представлять 1. Обратите внимание: здесь:

Точка знаний! ! !

То есть максимальный диапазон абсолютных значений чисел, которые может представлять число, составляет 2 ^ -1074 ~ 2 ^ (53 + 971)

потеря точности

Как упоминалось ранее, десятичные числа, хранящиеся в компьютере, сначала преобразуются в двоичные для хранения Давайте посмотрим на результаты преобразования 0,1 и 0,2 в двоичные:


(0.1)10 => (00011001100110011001(1001)...)2

(0.2)10 => (00110011001100110011(0011)...)2

Можно обнаружить, что после преобразования 0,1 и 0,2 в двоичные числа они оба являются числами бесконечного цикла.Как упоминалось ранее, мантисса может хранить только до 53 значащих цифр.В это время ее необходимо округлить, и правило этого округление в IEEE 754. Как определено в , конечное значимое число 0,1, которое может быть сохранено, равно


0001(1001)(1001)(1001)(1001)(1001)(1001)(1001)(1001)(1001)(1001)(1001)(1001)101
+
(0011)(0011)(0011)(0011)(0011)(0011)(0011)(0011)(0011)(0011)(0011)(0011)(0011)01
=
0100(1100)(1100)(1100)(1100)(1100)(1100)(1100)(1100)(1100)(1100)(1100)(1100)111

Обратите внимание, что 53-битный бит хранения означает, что можно сохранить 53 значащих цифры, поэтому начальный 0 не учитывается, а 53 значащие цифры должны быть взяты позже.

Окончательное двоичное число, преобразованное в десятичное, равно 0,300000000000000004 (если вы не верите, вы можете найти онлайн-инструмент для преобразования двоичных чисел, чтобы попробовать его.

резюме

Эта проблема потери точности была устранена, суммирована в одном предложении, используя компьютер для хранения двоично-десятичных чисел, и после того, как большая часть десятичного числа превращается в двоичное значение, возникает бесконечный цикл, есть компромиссы, которые то есть потеря точности.

максимально безопасное целое число

Вот прямая рекомендациястатья, что очень ясно об этом вопросе (в тексте есть ошибка, на которую будет указано ниже.

Если вам лень читать по-английски, можете прочитать мое резюме:

Двоичное число, соответствующее наибольшему безопасному целому числу 9007199254740991, показано на рисунке:

После сохранения всех 53 значащих цифр, если вы хотите представить большее число, вы можете добавить к показателю степени только одну цифру.В настоящее время, поскольку для мантиссы нет дополнительного места для хранения, вы можете добавить только 0.

Как показано на рисунке, когда бит степени равен 53, число, последняя цифра которого равна 0, может быть точно представлено, а число, последняя цифра которого равна 1, не может быть точно представлено. То есть соотношение, которое может и не может быть точно представлено, равно1:1.

Точно так же, когда показатель степени равен 54, только две последние цифры с мантиссом 00 могут быть точно представлены, то есть отношение, которое может быть точно представлено и не может быть точно представлено, равно1:3, когда эффективное количество цифр достигаетx(x>53), отношения, которые могут и не могут быть точно представлены, будут1 : 2^(x-53) - 1.

Можно предвидеть, что когда индекс становится все выше и выше, этот индекс будет увеличиваться экспоненциально, поэтому можно сказать, что целые числа, которые могут быть точно представлены между Number.MAX_SAFE_INTEGER ~ Number.MAX_VALUE, встречаются редко.

Я нашел ошибку в этой статье, в статье указано, что число 9007199254740998 не может быть представлено точно, на самом деле это так, в случае, когда бит экспоненты равен 53, четные числа могут быть представлены точно, нечетные числа не могут быть представлены точно, и не может быть представлено точно с помощью Наименьшее четное число, которое может быть точно представлено, должно быть, когда биты экспоненты равны 54, а последние две цифры мантиссы равны 0.

резюме

Причина, по которой существует концепция наибольшего безопасного целого числа, в основном связана со структурой хранения цифровых типов в компьютерах. После того, как биты мантиссы не заполнены нулями, целые числа, соответствующие избыточной мантиссе 1, не могут быть точно представлены.

Суммировать

Можно обнаружить, что результат вычисления числа с плавающей запятой или результат вычисления большого целого числа неверен, это можно объяснить тем фактом, что точность JS составляет всего 53 бита (мантисса может хранить только 53 значащие цифры) . Так как же мы решаем эти две проблемы в нашей повседневной работе?

Большое решение заключается в использованииmathjsПосмотрите на вывод Mathjs:


math.config({
    number: 'BigNumber',      
    precision: 64 
});

console.log(math.format(math.eval('0.1 + 0.2'))); // '0.3'

console.log(math.format(math.eval('0.23 * 0.34 * 0.92'))); // '0.071944'

console.log(math.format(math.eval('9007199254740991 + 2'))); // '9.007199254740993e+15'

На самом деле, случаев целочисленного переполнения очень мало.Большинство сценариев - расчеты с плавающей запятой.Если вы не хотите вводить mathjs для некоторых простых вычислений, вы также можете реализовать функцию операции самостоятельно (вам нужно подумать, нужно ли число выходит за пределы и когда числа представлены в виде экспоненциальной записи), если вам лень реализовывать это самостоятельно, вы можете использовать это меньше, чем 1knumber-precision, API этой библиотеки инструментов намного проще и уже может решить проблему вычисления чисел с плавающей запятой (если смотреть на код, то метод обработки чисел, превышающих Number.MAX_SAFE_INTEGER, заключается в выдаче предупреждения).

использованная литература

Для получения более интересного контента, пожалуйста, обратите внимание на публичный аккаунт WeChat фронтенд-команды NetEase Koala.

ps: После волны рекламы, фронтенд Netease Koala набирает сотрудников~~~ ЗаинтересованоНажмите на меня, чтобы отправить резюме

image