103

Does this for loop ever stop?

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

If so, when and why? I was told that it stops, but I was given no reason for that.

Upddate

As part of the investigation I've written quite lengthy and detailed article that explains everything what's going on under the hood - Here is what you need to know about JavaScript’s Number type

Community
  • 1
  • 1
Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
  • 5
    It wont stop. try executing this peice of code. for (var i=0; 1/i > 0; i++) { console.log(i) } – Sourabh Agrawal Jun 15 '16 at 05:54
  • 3
    Relevant: [Calculate time taken to break AES key](http://security.stackexchange.com/a/82412/17306) , from [security.se], originally by [Schneier on Security](https://www.schneier.com/blog/archives/2009/09/the_doghouse_cr.html). – Kobi Jun 15 '16 at 06:27
  • 1
    also [Use multiple computers for faster brute force](http://security.stackexchange.com/a/116573/17306) – Kobi Jun 15 '16 at 06:32
  • 3
    Number.MAX_VALUE + 9.979202e291 == "Infinity" and 1/ (NaN or 'Infinity' or 'undefined') > 0 == false. – askeet Jun 15 '16 at 06:54
  • 6
    Will Javascript ignore that loop because it has no statements inside? i.e. Optimize it away? I know there are some compiled languages that would. – Brian J Jun 15 '16 at 12:54
  • 3
    @askeet, as [gotnull](http://stackoverflow.com/a/37827271/390158) and others point out below, we never reach Infinity by incrementing repeatedly, instead getting caught in a loop after `Number.MAX_SAFE_INTEGER + 1`. – LSpice Jun 15 '16 at 13:57
  • @L Spice I know it very large value for loop, but it only theoretically possible value for finish loop. – askeet Jun 15 '16 at 16:40
  • 2
    @askeet: It's not theoretically possible to finish the loop, because JavaScript's numbers are not *real* numbers, they're limited by their representation. In the *theory* (well, reality) of IEEE-754 double-precision floating point, the loop never finishes. – T.J. Crowder Jun 15 '16 at 22:47
  • @BrianJ: It's a good question, but no, it won't ignore it. And it can't optimize it away, because doing so would be incorrect behavior. Correctly implemented, it *must* loop, and loop forever (barring the host terminating it). – T.J. Crowder Jun 15 '16 at 22:49
  • 2
    @HongOoi "Does a 'for' loop stop if the condition involves dividing by 0" is not a good title; whether or not `i` starts from 0 or e.g. 1 doesn't change the characteristics of the given question. I therefore reverted that edit. – le_m Jun 16 '16 at 15:54
  • 1
    It should be noted that some languages (not javascript) wrap integers around, once they reach their max value, they become negative their max value and start going back towards 0, at any negative value this for loop will stop. – Albert Renshaw Jun 30 '16 at 17:51
  • @Maximus Any idea on [How to reset data and view of child input on click of parent's button click?](https://stackoverflow.com/q/44597080/2404470) – Zameer Ansari Jul 20 '17 at 20:50

3 Answers3

128

(I'm not a fan of meta-content, but: gotnull's and le_m's answers are both correct and useful. They were originally, and are even more so with the edits made after this Community Wiki was posted. The original motivation for this CW is largely gone as a result of those edits, but it remains useful, so... Also: While there are only a couple of authors listed, many other community members have helped greatly with comments which have been folded in and cleaned up. This isn't just a CW in name.)


The loop won't stop in a correctly-implemented JavaScript engine. (The engine's host environment might eventually terminate it because it's endless, but that's another thing.)

Here's why:

  1. Initially, when i is 0, the condition 1/i > 0 is true because in JavaScript, 1/0 is Infinity, and Infinity > 0 is true.

  2. After that, i will be incremented and continue to grow as a positive integer value for a long time (a further 9,007,199,254,740,991 iterations). In all of those cases, 1/i will remain > 0 (although the values for 1/i get really small toward the end!) and so the loop continues up to and including the loop where i reaches the value Number.MAX_SAFE_INTEGER.

  3. Numbers in JavaScript are IEEE-754 double-precision binary floating point, a fairly compact format (64 bits) which provides for fast calculations and a vast range. It does this by storing the number as a sign bit, an 11-bit exponent, and a 52-bit significand (although through cleverness it actually gets 53 bits of precision). It's binary (base 2) floating point: The significand (plus some cleverness) gives us the value, and the exponent gives us the magnitude of the number.

    Naturally, with just so many significant bits, not every number can be stored. Here is the number 1, and the next highest number after 1 that the format can store, 1 + 2-52 ≈ 1.00000000000000022, and the next highest after that 1 + 2 × 2-52 ≈ 1.00000000000000044:

       +--------------------------------------------------------------- sign bit
      / +-------+------------------------------------------------------ exponent
     / /        |  +-------------------------------------------------+- significand
    / /         | /                                                  |
    0 01111111111 0000000000000000000000000000000000000000000000000000
                    = 1
    0 01111111111 0000000000000000000000000000000000000000000000000001
                    ≈ 1.00000000000000022
    0 01111111111 0000000000000000000000000000000000000000000000000010
                    ≈ 1.00000000000000044
    

    Note the jump from 1.00000000000000022 to 1.00000000000000044; there's no way to store 1.0000000000000003. That can happen with integers, too: Number.MAX_SAFE_INTEGER (9,007,199,254,740,991) is the highest positive integer value that the format can hold where i and i + 1 are both exactly representable (spec). Both 9,007,199,254,740,991 and 9,007,199,254,740,992 can be represented, but the next integer, 9,007,199,254,740,993, cannot; the next integer we can represent after 9,007,199,254,740,992 is 9,007,199,254,740,994. Here are the bit patterns, note the rightmost (least significant) bit:

       +--------------------------------------------------------------- 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)
    

    Remember, the format is base 2, and with that exponent the least significant bit is no longer fractional; it has a value of 2. It can be off (9,007,199,254,740,992) or on (9,007,199,254,740,994); so at this point, we've started to lose precision even at the whole number (integer) scale. Which has implications for our loop!

  4. After completing the i = 9,007,199,254,740,992 loop, i++ gives us ... i = 9,007,199,254,740,992 again; there's no change in i, because the next integer can't be stored and the calculation ends up rounding down. i would change if we did i += 2, but i++ can't change it. So we've reached steady-state: i never changes, and the loop never terminates.

Here are the various relevant calculations:

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's 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
Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
77

Answer:

The condition 1/i > 0 will always evaluate to true:

  • Initially it's true because 1/0 evaluates to Infinity and Infinity > 0 is true

  • It stays true since 1/i > 0 is true for all i < Infinity and i++ never reaches Infinity.

Why does i++ never reach Infinity? Due to the limited precision of the Number datatype, there is a value for which i + 1 == i:

9007199254740992 + 1 == 9007199254740992 // true

Once i reaches that value (which corresponds to Number.MAX_SAFE_INTEGER + 1), it will stay the same even after i++.

We therefore have an infinite loop.


Appendix:

Why is 9007199254740992 + 1 == 9007199254740992?

JavaScript's Number datatype is actually an 64-bit IEEE 754 double precision float. Each Number is disassembled and stored as three parts: 1-bit sign, 11-bit exponent, and 52-bit mantissa. Its value is -1 sign × mantissa × 2 exponent.

How is 9007199254740992 represented? As 1.0 × 2 53, or in binary:

enter image description here

Incrementing the mantissa's least significant bit, we get the next higher number:

enter image description here

The value of that number is 1.00000000000000022… × 2 53 = 9007199254740994

What does that mean? Number can either be 9007199254740992 or 9007199254740994, but nothing in between.

Now, which one shall we chose to represent 9007199254740992 + 1? The IEEE 754 rounding rules give the answer: 9007199254740992.

Community
  • 1
  • 1
le_m
  • 19,302
  • 9
  • 64
  • 74
  • 9
    short and correct, better than the current accepted answer – AlexWien Jun 15 '16 at 19:42
  • @AlexWien The accepted answer is a community wiki accepted answer. – gotnull Jun 15 '16 at 23:26
  • 2
    I don't know the term "community wiki accpeted" answer. What has this to do with stackoverflow? If this is a foreign link, a link should be provided. Accepted answers on stackoverflow can always change, the status accpeted is not final. – AlexWien Jun 16 '16 at 13:32
  • "Why does i++ never reach Infinity? Due to the limited precision of the Number datatype..." <- Surely it would never reach infinity, even with an infinite-precision number type.. You know, because you can't count to infinity :P – Blorgbeard Jun 21 '16 at 21:06
  • 1
    @Blorgbeard You *can* count to Infinity with limited precision doubles, you just have to increment by a much bigger number than 1, e. g. `for (var i = 0; i < Infinity; i += 1E306);`. But I get where you're coming from ;) – le_m Jun 21 '16 at 23:00
27

The Number.MAX_SAFE_INTEGER constant represents the maximum safe integer in JavaScript. The MAX_SAFE_INTEGER constant has a value of 9007199254740991. The reasoning behind that number is that JavaScript uses double-precision floating-point format numbers as specified in IEEE 754 and can only safely represent numbers between -(253 - 1) and 253 - 1.

Safe in this context refers to the ability to represent integers exactly and to correctly compare them. For example, Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 will evaluate to true, which is mathematically incorrect. See Number.isSafeInteger() for more information.

Because MAX_SAFE_INTEGER is a static property of Number, you always use it as Number.MAX_SAFE_INTEGER, rather than as a property of a Number object you created.

UPDATE:

Someone in an answer that was deleted mentioned: i will never reach infinity. Once it reaches Number.MAX_SAFE_INTEGER, i++ doesn't increment the variable anymore. This is in fact not correct.

@T.J. Crowder comments that i = Number.MAX_SAFE_INTEGER; i++; i == Number.MAX_SAFE_INTEGER; is false. But the next iteration reaches an unchanging state, so the answer in main is correct.

i in the example never reaches Infinity.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
gotnull
  • 26,454
  • 22
  • 137
  • 203