javascript - 这个 'for' 循环是否停止,为什么/为什么不停止?对于 (var i=0; 1/i > 0; i++) { }

标签 javascript

这个 for 循环会停止吗?

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

如果是,何时以及为什么?有人告诉我它停止了,但我没有得到任何理由。

更新

作为调查的一部分,我写了一篇相当冗长和详细的文章,解释了幕后发生的一切 - Here is what you need to know about JavaScript’s Number type

最佳答案

(我不是元内容的粉丝,但是:gotnull'sle_m's 答案既正确又有用。它们最初是,而且更是如此在发布此社区 Wiki 后进行的编辑。由于这些编辑,此 CW 的原始动机在很大程度上消失了,但它仍然有用,所以...另外:虽然只列出了几个作者,但还有许多其他作者社区成员对已折叠和清理的评论提供了很大帮助。这不仅仅是名义上的 CW。)


循环不会在正确实现的 JavaScript 引擎中停止。 (引擎的宿主环境最终可能会终止它,因为它是无穷无尽的,但那是另一回事。)

原因如下:

  1. 最初,当 i0 时,条件 1/i > 0 为真,因为在 JavaScript 中, 1/0InfinityInfinity > 0 为真。

  2. 之后,i 将递增并作为正整数值持续增长很长一段时间(进一步的 9,007,199,254,740,991 次迭代)。在所有这些情况下,1/i 将保持 > 0(尽管 1/i 的值变得真正 接近尾声!)因此循环继续进行并包括 i 达到值 Number.MAX_SAFE_INTEGER 的循环。

  3. JavaScript 中的数字是 IEEE-754 double 二进制 float ,这是一种相当紧凑的格式(64 位),可提供快速计算和广泛的范围。它通过将数字存储为一个符号位、一个 11 位指数和一个 52 位有效数来实现这一点(尽管通过巧妙它实际上获得了 53 位精度)。它是二进制(以 2 为底) float :有效数字(加上一些巧妙的技巧)为我们提供了值,指数为我们提供了数字的大小。

    自然地,只有这么多有效位,并不是每个数字都可以存储。这里是数字 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
    

    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

关于javascript - 这个 'for' 循环是否停止,为什么/为什么不停止?对于 (var i=0; 1/i > 0; i++) { },我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37827073/

相关文章:

javascript - 未定义的函数

javascript - 在 JavaScript 中,当一次声明多个变量时,只使用一个 var 语句是否有内存优势?

javascript - Mmenu 垂直子菜单 w/JQuery 切换

javascript - 为什么从 Canvas 中检索到的像素都是黑色的?

javascript - 处理日期选择器日历小部件中的日期值

javascript - 文本点击显示(功能)

javascript - react JS : Populating a rsuite dropdown from a json object

javascript - 将多维数组转换为 HTML 表

javascript - Angularjs 以各种形式动态添加表单字段

javascript - Angular 和 RxJs 结合了两个 http 请求