此for
循环是否会停止?
for (var i=0; 1/i > 0; i++) {
}
如果是的话,何时以及为何?我被告知它停止了,但我没有理由这样做。
作为调查的一部分,我写了很长很详细的文章,解释了幕后发生的一切 - Here is what you need to know about JavaScript’s Number type
答案 0 :(得分:128)
(我不是元内容的粉丝,但是:gotnull's和le_m's答案都是正确且有用的。它们本来是,并且更是如此在社区Wiki发布之后进行的编辑。由于这些编辑,这个CW的原始动机很大程度上消失了,但它仍然有用,所以......还有:虽然只有几个作者在列出的情况下,许多其他社区成员对已经折叠和清理的评论提供了很大的帮助。这不仅仅是CW的名称。)
循环不会在正确实现的JavaScript引擎中停止。 (引擎的主机环境可能最终会终止它,因为它是无穷无尽的,但这是另一回事。)
原因如下:
最初,当i
为0
时,条件1/i > 0
为真,因为在JavaScript中,1/0
为Infinity
,{{1是真的。
之后,Infinity > 0
将递增并继续增长为正整数值很长时间(另外9,007,199,254,740,991次迭代)。在所有这些情况下,i
将保持1/i
(尽管> 0
的值会在结束时真正小!)然后循环继续到并包括1/i
达到值i
的循环。
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_INTEGER
和i
都可以完全表示(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);所以在这一点上,即使在整数(整数)范围内,我们也开始失去精度。这对我们的循环有影响!
完成i + 1
循环后,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
答案 1 :(得分:79)
条件1/i > 0
将始终评估为true:
最初是正确的,因为1/0
评估为Infinity
且Infinity > 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 ,或二进制:
增加尾数的最低有效位,我们得到下一个更高的数字:
该数字的值是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_INTEGER
是Number
的静态属性,因此您始终将其用作Number.MAX_SAFE_INTEGER
,而不是您创建的Number
对象的属性。
<强>更新强>
在提及已删除的答案中有人提到:i
永远不会达到无限。一旦达到Number.MAX_SAFE_INTEGER
,i++
就不再增加变量。事实上这不正确。
@ T.J。 Crowder评论i = Number.MAX_SAFE_INTEGER; i++; i == Number.MAX_SAFE_INTEGER;
是false
。但是下一次迭代达到了一个不变的状态,所以主要的答案是正确的。
i
永远不会到达Infinity
。