(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:
-
Initially, when
i
is0
, the condition1/i > 0
is true because in JavaScript,1/0
isInfinity
, andInfinity > 0
is true. -
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 for1/i
get really small toward the end!) and so the loop continues up to and including the loop wherei
reaches the valueNumber.MAX_SAFE_INTEGER
. -
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 / / "https://stackoverflow.com/" 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 wherei
andi + 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 / / "https://stackoverflow.com/" 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!
-
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 ini
, because the next integer can’t be stored and the calculation ends up rounding down.i
would change if we didi += 2
, buti++
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