Заканчивается ли это для цикла, и почему/почему нет? для (var я = 0; 1/i> 0; я ++) {}

Заканчивается ли этот цикл for?

for (var i=0; 1/i > 0; i++) {
}

Если да, то когда и почему? Мне сказали, что он останавливается, но мне не было причин для этого.

Upddate

В рамках расследования я написал довольно длинную и подробную статью, в которой объясняется все, что происходит под капотом - Вот что вам нужно знать о JavaScripts Тип номера

Ответ 1

(Я не поклонник мета-контента, но: gotnull и ответы le_m как правильные, так и полезные. Они были изначально и даже более с изменениями, сделанными после этой публикации в Community Wiki. Первоначальная мотивация для этого CW в значительной степени исчезла из-за этих изменений, но это остается полезной, поэтому... Кроме того: хотя есть только несколько авторов, перечисленных, многие другие члены сообщества очень помогли с комментариями, которые были сложены и очищены. Это не просто имя CW.)


Цикл не остановится в правильно реализованном JavaScript-движке. (Исходная среда движка может в конечном итоге прекратить ее, потому что она бесконечна, но это другая вещь.)

Вот почему:

  • Изначально, когда i равно 0, условие 1/i > 0 истинно, потому что в JavaScript 1/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

Ответ 2

Ответ:

Условие 1/i > 0 всегда будет иметь значение true:

  • Изначально это верно, потому что 1/0 оценивается как Infinity и Infinity > 0 истинно

  • Он остается истинным, поскольку 1/i > 0 true для всех i < Infinity и i++ никогда не достигает Infinity.

Почему i++ никогда не достигает Infinity? Из-за ограниченной точности типа данных Number существует значение, для которого i + 1 == i:

9007199254740992 + 1 == 9007199254740992 // true

Как только i достигает этого значения (что соответствует Number.MAX_SAFE_INTEGER + 1), оно останется неизменным даже после i++.

Следовательно, мы имеем бесконечный цикл.


Приложение:

Почему 9007199254740992 + 1 == 9007199254740992?

JavaScript Number datatype на самом деле является 64-битным IEEE 754 double precision float. Каждый Number разбирается и сохраняется как три части: 1-битный знак, 11-разрядный показатель и 52-битная мантисса. Его значение равно -1 sign × mantissa × 2 exponent.

Как представлен 9007199254740992? Как 1.0 × 2 53 или в двоичном формате:

enter image description here

Увеличивая младший значащий бит мантиссы, мы получаем следующее большее число:

enter image description here

Значение этого числа: 1.00000000000000022... × 2 53= 9007199254740994

Что это значит? Number может быть 900719925474099 2 или 900719925474099 4, но ничего между ними.

Теперь, какой из них мы должны выбрать, чтобы представить 900719925474099 2 + 1? правила округления IEEE 754 дают ответ: 900719925474099 2.

Ответ 3

Константа Number.MAX_SAFE_INTEGER представляет максимальное безопасное целое число в JavaScript. Константа MAX_SAFE_INTEGER имеет значение 9007199254740991. Обоснованием этого числа является то, что JavaScript использует числа с плавающей запятой с двойной точностью, как указано в IEEE 754 и может только безопасно представлять числа между - (2 53 - 1) и 2 53 - 1.

Безопасный в этом контексте относится к способности точно представлять целые числа и правильно сравнивать их. Например, Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 будет оцениваться как true, что является математически неверным. Подробнее см. Number.isSafeInteger().

Поскольку MAX_SAFE_INTEGER является статическим свойством Number, вы всегда используете его как Number.MAX_SAFE_INTEGER, а не как свойство созданного вами объекта Number.

UPDATE:

Кто-то в ответ, который был удален: i никогда не достигнет бесконечности. Когда он достигнет Number.MAX_SAFE_INTEGER, i++ больше не увеличивает эту переменную. Это действительно неверно.

@T.J. Кроудер комментирует, что i = Number.MAX_SAFE_INTEGER; i++; i == Number.MAX_SAFE_INTEGER; есть false. Но следующая итерация достигает неизменного состояния, поэтому ответ в основном правильный.

i в примере никогда не достигает Infinity.