这个'for'循环停止了,为什么/为什么不呢? for(var i = 0; 1 / i> 0; i ++){}

时间:2016-06-15 05:48:26

标签: javascript

for循环是否会停止?

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

如果是的话,何时以及为何?我被告知它停止了,但我没有理由这样做。

Upddate

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

3 个答案:

答案 0 :(得分:128)

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

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

原因如下:

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

  2. 之后,Infinity > 0将递增并继续增长为正整数值很长时间(另外9,007,199,254,740,991次迭代)。在所有这些情况下,i将保持1/i(尽管> 0的值会在结束时真正小!)然后循环继续到并包括1/i达到值i的循环。

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

    当然,只有这么多有效位,不是每个数字都可以存储。这是数字1,格式可以存储的1之后的下一个最高数字,1 + 2 -52 ≈1.00000000000000022,然后是1 + 2×2 -52之后的下一个最高数字≈100000000000000044:

       +--------------------------------------------------------------- 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)是格式可以容纳的最高正整数值,其中Number.MAX_SAFE_INTEGERi都可以完全表示(spec) 。可以表示9,007,199,254,740,991和9,007,199,254,740,992,但是 next 整数,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);所以在这一点上,即使在整数(整数)范围内,我们也开始失去精度。这对我们的循环有影响!

  4. 完成i + 1循环后,i = 9,007,199,254,740,992再次向我们提供... i++; i = 9,007,199,254,740,992没有变化,因为无法存储下一个整数,并且计算最终向下舍入。如果我们i ii += 2会改变,i++会改变它。所以我们已经达到稳态:i永远不会改变,循环永远不会终止。

  5. 以下是各种相关计算:

    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

答案 1 :(得分:79)

答案:

条件1/i > 0将始终评估为true:

  • 最初是正确的,因为1/0评估为InfinityInfinity > 0为真

  • 由于1/i > 0对所有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数据类型实际上是64位IEEE 754 double precision float。每个Number被反汇编并存储为三个部分:1位符号,11位指数和52位尾数。其值为-1 sign ×尾数×2 指数

如何代表 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 rounding rules给出答案:900719925474099 2

答案 2 :(得分:27)

Number.MAX_SAFE_INTEGER常量表示JavaScript中的最大安全整数。 MAX_SAFE_INTEGER常量的值为9007199254740991。这个数字背后的原因是JavaScript使用double-precision floating-point format numbers中指定的IEEE 754,并且只能安全地表示 - (2 53 - 1)和2 53 <之间的数字/ sup> - 1。

此上下文中的安全性是指能够准确表示整数并正确比较它们的能力。例如,Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2将评估为true,这在数学上是不正确的。有关详细信息,请参阅Number.isSafeInteger()

由于MAX_SAFE_INTEGERNumber的静态属性,因此您始终将其用作Number.MAX_SAFE_INTEGER,而不是您创建的Number对象的属性。

<强>更新

在提及已删除的答案中有人提到:i永远不会达到无限。一旦达到Number.MAX_SAFE_INTEGERi++就不再增加变量。事实上这正确。

@ T.J。 Crowder评论i = Number.MAX_SAFE_INTEGER; i++; i == Number.MAX_SAFE_INTEGER;false。但是下一次迭代达到了一个不变的状态,所以主要的答案是正确的。

示例中的

i永远不会到达Infinity