Ответ 1
(Я не поклонник мета-контента, но: gotnull и ответы le_m как правильные, так и полезные. Они были изначально и даже более с изменениями, сделанными после этой публикации в Community Wiki. Первоначальная мотивация для этого CW в значительной степени исчезла из-за этих изменений, но это остается полезной, поэтому... Кроме того: хотя есть только несколько авторов, перечисленных, многие другие члены сообщества очень помогли с комментариями, которые были сложены и очищены. Это не просто имя CW.)
Цикл не остановится в правильно реализованном JavaScript-движке. (Исходная среда движка может в конечном итоге прекратить ее, потому что она бесконечна, но это другая вещь.)
Вот почему:
-
Изначально, когда
i
равно0
, условие1/i > 0
истинно, потому что в JavaScript1/0
естьInfinity
, аInfinity > 0
- true. -
После этого
i
будет увеличиваться и продолжать расти как положительное целое значение в течение длительного времени (еще 9 007 199 254 740 991 итераций). Во всех этих случаях1/i
останется> 0
(хотя значения для1/i
получаются действительно малыми к концу!), И поэтому цикл продолжается до и включает цикл, в которомi
достигает значенияNumber.MAX_SAFE_INTEGER
. -
Числами в JavaScript являются двоичная с плавающей запятой двойной точности IEEE-754, довольно компактный формат (64 бит), который обеспечивает быстрые вычисления и широкий диапазон. Он делает это, сохраняя число в виде знакового бита, 11-битного экспонента и 52-битного значения (хотя благодаря умению он фактически получает 53 бит точности). Это двоичная (базовая 2) с плавающей запятой: Значимость (плюс некоторая умность) дает нам значение, а показатель показывает нам величину числа.
Естественно, что с таким количеством значимых битов может сохраняться не каждый номер. Вот число 1 и следующее наибольшее число после 1, которое может хранить формат, 1 + 2 -52 ≈ 1.00000000000000022, а следующий самый высокий после этого 1 + 2 × 2 -52 ≈ 1.00000000000000044:
+--------------------------------------------------------------- sign bit / +-------+------------------------------------------------------ exponent / / | +-------------------------------------------------+- significand / / | / | 0 01111111111 0000000000000000000000000000000000000000000000000000 = 1 0 01111111111 0000000000000000000000000000000000000000000000000001 ≈ 1.00000000000000022 0 01111111111 0000000000000000000000000000000000000000000000000010 ≈ 1.00000000000000044
Обратите внимание на переход от 1.00000000000000022 до 1.00000000000000044; нет способа хранить 1.0000000000000003. Это может произойти и с целыми числами:
Number.MAX_SAFE_INTEGER
(9,007,199,254,740,991) - это наивысшее положительное целочисленное значение, которое может содержать формат, гдеi
иi + 1
оба точно представляются (spec). Как 9 007 199 254 740 991, так и 9 007 199 254 740 992 могут быть представлены, но следующее целое число - 9 007 199 254 740 993 не может; следующее целое число, которое мы можем представить после того, как 9 007 199 254 740 992 составляет 9 007 199 254 740 994. Ниже приведены битовые шаблоны, обратите внимание на самый правый (наименее значимый) бит:+--------------------------------------------------------------- sign bit / +-------+------------------------------------------------------ exponent / / | +-------------------------------------------------+- significand / / | / | 0 10000110011 1111111111111111111111111111111111111111111111111111 = 9007199254740991 (Number.MAX_SAFE_INTEGER) 0 10000110100 0000000000000000000000000000000000000000000000000000 = 9007199254740992 (Number.MAX_SAFE_INTEGER + 1) x xxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 9007199254740993 (Number.MAX_SAFE_INTEGER + 2) can't be stored 0 10000110100 0000000000000000000000000000000000000000000000000001 = 9007199254740994 (Number.MAX_SAFE_INTEGER + 3)
Помните, что формат является базой 2, и с этим показателем младший значащий бит больше не является дробным; он имеет значение 2. Он может быть выключен (9 007 199 254 740 992) или на (9 007 199 254 740 994); поэтому на этом этапе мы начали терять точность даже в масштабе всего числа (целочисленного). Что имеет значение для нашего цикла!
-
После завершения цикла
i = 9,007,199,254,740,992
i++
снова возвращает нам...i = 9,007,199,254,740,992
; там нет изменений вi
, потому что следующее целое число не может быть сохранено, и вычисление заканчивается округлением вниз.i
изменится, если мы сделалиi += 2
, ноi++
не может его изменить. Итак, мы достигли установившегося состояния:i
никогда не изменяется, и цикл никогда не заканчивается.
Вот несколько соответствующих расчетов:
if (!Number.MAX_SAFE_INTEGER) {
// Browser doesn't have the Number.MAX_SAFE_INTEGER
// property; shim it. Should use Object.defineProperty
// but hey, maybe it so old it doesn't have that either
Number.MAX_SAFE_INTEGER = 9007199254740991;
}
var i = 0;
console.log(i, 1/i, 1/i > 0); // 0, Infinity, true
i++;
console.log(i, 1/i, 1/i > 0); // 1, 1, true
// ...eventually i is incremented all the way to Number.MAX_SAFE_INTEGER
i = Number.MAX_SAFE_INTEGER;
console.log(i, 1/i, 1/i > 0); // 9007199254740991 1.1102230246251568e-16, true
i++;
console.log(i, 1/i, 1/i > 0); // 9007199254740992 1.1102230246251565e-16, true
i++;
console.log(i, 1/i, 1/i > 0); // 9007199254740992 1.1102230246251565e-16, true (no change)
console.log(i == i + 1); // true