这个“ for”循环会停止吗?为什么/为什么不停止?对于(var i = 0; 1 / i> 0; i ++){}


104

这个for循环会停止吗?

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

如果是这样,什么时候以及为什么?有人告诉我它停止了,但是我没有理由这么做。

更新日期

作为调查的一部分,我写了一篇冗长而详细的文章,解释了幕后工作的一切- 这是您需要了解的JavaScript Number类型的知识


5
它不会停止。尝试执行这段代码。for(var i = 0; 1 / i> 0; i ++){console.log(i)}
Sourabh Agrawal


3
Number.MAX_VALUE + 9.979202e291 ==“ Infinity”和1 /(NaN或'Infinity'或'undefined')> 0 ==否。
askeet

6
JavaScript会因为里面没有语句而忽略该循环吗?即优化它?我知道有些编译语言可以。
Brian J

3
@askeet,如gotnull和其他人在下面指出的那样,我们永远不会通过反复递增来达到Infinity,而是在之后陷入循环Number.MAX_SAFE_INTEGER + 1
LSpice

Answers:


128

(我不喜欢meta内容,但是:gotnullle_m的答案都是正确且有用的。它们本来就是正确的,在发布此Community Wiki之后所做的编辑更是如此。此CW的原始动机是这些编辑在很大程度上使它消失了,但是它仍然有用,所以...另外:虽然只列出了几位作者,但许多其他社区成员对已被折叠和整理的评论也提供了很大的帮助。不只是名字上的CW。)


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

原因如下:

  1. 最初i是什么时候0,条件1/i > 0为true,因为在JavaScript中,1/0is InfinityInfinity > 0true。

  2. 之后,i将递增,并在很长一段时间内继续增长为正整数值(进一步的9,007,199,254,740,991迭代)。在所有这些情况下,1/i都将保留> 0(尽管1/iget 的值真正朝着端小!),继续循环直至并包括在那里,循环等i达到值Number.MAX_SAFE_INTEGER

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

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

       + ------------------------------------------------- --------------符号位
      / + ------- + ---------------------------------------- --------------指数
     / / | + ------------------------------------------------- +-有效
    / / | / |
    0 01111111111 0000000000000000000000000000000000000000000000000000000000
                    = 1
    0 01111111111 0000000000000000000000000000000000000000000000000000000001
                    ≈1.00000000000000022
    0 01111111111 0000000000000000000000000000000000000000000000000000000010
                    ≈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。这是位模式,请注意最右边(最低有效)位:

       + ------------------------------------------------- --------------符号位
      / + ------- + ---------------------------------------- --------------指数
     / / | + ------------------------------------------------- +-有效
    / / | / |
    0 10000110011 1111111111111111111111111111111111111111111111111111111
                    = 9007199254740991(Number.MAX_SAFE_INTEGER)
    0 10000110100 0000000000000000000000000000000000000000000000000000000000
                    = 9007199254740992(Number.MAX_SAFE_INTEGER + 1)
    x xxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
                      9007199254740993(Number.MAX_SAFE_INTEGER + 2)无法存储
    0 10000110100 0000000000000000000000000000000000000000000000000000000001
                    = 9007199254740994(Number.MAX_SAFE_INTEGER + 3)
    

    请记住,格式为2,并且该指数的最低有效位不再是小数。它的值为2。可以关闭(9,007,199,254,740,992)或打开(9,007,199,254,740,994);因此,在这一点上,即使在整数(整数)范围内,我们也开始失去精度。这对我们的循环有影响!

  4. 完成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'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


79

回答:

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

  • 最初是真的,因为1/0评估为Infinity,并Infinity > 0为真

  • 它保持真实,因为1/i > 0对所有人而言都是真实的i < Infinityi++永远不会达到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双精度float。每个组件都Number被分解并存储为三部分:1位符号,11位指数和52位尾数。其值为-1 符号 ×尾数×2 指数

9007199254740992如何表示?为1.0×2 53或二进制:

在此处输入图片说明

递增尾数的最低有效位,我们得到下一个更高的数字:

在此处输入图片说明

该数字的值是1.00000000000000022…×2 53 = 9007199254740994

那是什么意思?Number可以是900719925474099 2或900719925474099 4,但两者之间什么也不能。

现在,我们应该选择哪一个代表900719925474099 2 + 1?在IEEE 754舍入规则给出了答案:900719925474099 2


9
简短而正确,胜于当前接受的答案
AlexWien

@AlexWien可接受的答案是社区Wiki可接受的答案。
fulvio

2
我不知道“增强社区Wiki”一词的答案。这与stackoverflow有什么关系?如果这是外部链接,则应提供一个链接。在stackoverflow上接受的答案总是可以更改的,获得的状态不是最终的。
AlexWien

“为什么i ++永远不会达到无穷大?由于Number数据类型的精度有限...” <-当然,即使具有无限精度的数字类型,它也永远不会达到无穷大。无限:P
Blorgbeard在

1
@Blorgbeard您可以以有限的精度倍数计数到Infinity,您只需要增加一个比1大得多的数字即可,例如for (var i = 0; i < Infinity; i += 1E306);。但是我知道你来自哪里;)
le_m

27

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创建的对象的属性。

更新:

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

@TJ Crowder评论i = Number.MAX_SAFE_INTEGER; i++; i == Number.MAX_SAFE_INTEGER;false。但是下一次迭代达到不变状态,因此main中的答案是正确的。

i在这个例子中永远都无法实现Infinity


2
具体来说9007199254740992 + 19007199254740992
科比

1
@GerardoFurtado我会想象会的。
fulvio

1
@GerardoFurtado for (var i=0; NaN > 0; i++) { console.log(i); }将不会产生任何东西。
fulvio

2
@GerardoFurtado:在这种情况下,循环将停止。因为第一个测试(1/i > 0)将为false,因为if i01/iis NaN和false,所以根本不会输入循环体NaN > 0
TJ Crowder

1
@TJCrowder我已经更新了答案。感谢您指出了这一点!
fulvio
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.