Почему увеличение Number.MAX_VALUE на 1 не дает infinity?
По какой причине? Если Number.MAX_VALUE это максимально возможное число.
Ответы (1 шт):
Давайте взглянем на число с плавающей точкой двойной точности формата IEEE 754 , предшествующее Number.MAX_VALUE и на само Number.MAX_VALUE в бинарном виде:
prev(MAX_VALUE): 0 | 11111111110 | 1111111111111111111111111111111111111111111111111110
MAX_VALUE : 0 | 11111111110 | 1111111111111111111111111111111111111111111111111111
S | E | M
На первый взгляд, разница между ними не велика: отличие всего в одном самом младшем бите. Преобразуем бинарные представления в точные вещественные значения. Формула преобразования выглядит следующим образом:
exact_value := (-1)^S * 2^(E-1023) * (1 + M / 2^52)
где S, E и M — целочисленные значения, которые мы получим, если будем интерпретировать соответствующие биты в бинарном представлении как целые числа, записанные в двоичной системе счисления.
prev(MAX_VALUE) == (-1)^0 * 2^(2046-1023) * (1 + (2^52-2) / 2^52) == 2^1024 - 4 * 2^970
MAX_VALUE == (-1)^0 * 2^(2046-1023) * (1 + (2^52-1) / 2^52) == 2^1024 - 2 * 2^970
Теперь найдём разницу между этими подряд идущими числами с плавающей точкой:
MAX_VALUE - prev(MAX_VALUE) === 2^1024 - 2 * 2^970 - [2^1024 - 4 * 2^970] == 2 * 2^970
2 * 2^970 — это огромная величина:
console.log( 2n * 2n ** 970n );
Никакое вещественное число из интервала (prev(MAX_VALUE), MAX_VALUE) типом Number не представимо.
Тогда каков результат вычисления выражения prev(MAX_VALUE) + 1 или MAX_VALUE - 100?
Результат следующих операций над числами с плавающей точкой — сложение, вычитание, умножение, деление и извлечение квадратного корня — должен быть таким, как если бы вычисления производились по следующему алгоритму:
- Найти точный вещественный результат операции.
- Округлить точный результат до ближайшего числа с плавающей точкой. Если точный результат находится ровно посередине между двумя числами с плавающей точкой, то должно быть выбрано то, младший бит которого равен нулю.
Итак, половина разности между prev(MAX_VALUE) и MAX_VALUE равна 2 * 2^970 / 2 == 2^970. Следовательно число prev(MAX_VALUE) + 2^970 есть граница, определяющая в какую сторону будет округлён точный результат.
Если точный вещественный результат арифметической операции принадлежит отрезку [prev(MAX_VALUE), prev(MAX_VALUE) + 2^970], то он будет округлён до prev(MAX_VALUE).
Если результат принадлежит полуинтервалу (prev(MAX_VALUE) + 2^970, MAX_VALUE], то он будет округлён до MAX_VALUE.
Теперь взглянем на битовый паттерн, кодирующий Infinity:
0 | 11111111111 | 0000000000000000000000000000000000000000000000000000
Заметим, что он в точности равен битовому паттерну, кодирующему MAX_VALUE после прибавления единицы. Если воспользоваться приведённой ранее формулой для преобразования в вещественное значение, то мы получим 2^1024.
Именно значение лежащее посередине между MAX_VALUE == 2^1024 - 2 * 2^970 и 2^1024, и равное MAX_VALUE + 2^970 является границей при выборе направления округления величин больших, чем MAX_VALUE.
Точное значение, принадлежащее полуинтервалу [MAX_VALUE, MAX_VALUE + 2^970), округляется до MAX_VALUE.
Значение, равное MAX_VALUE + 2^970 или большее, округляется до Infinity.
Точный результат выражения Number.MAX_VALUE + 1 принадлежит полуинтервалу [MAX_VALUE, MAX_VALUE + 2^970), следовательно округляется до MAX_VALUE.