为什么JavaScript中两个不同的数字相等?

rev*_*rev 44 javascript numbers

当我突然决定尝试这个时,我一直在使用JavaScript控制台.

0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

令人惊讶的是,他们是平等的: 奇怪的输出

为什么会这样?它们的数字明显不同(即使0xFFFF...FFFF是一个数字更短)

如果我添加F0xFFFF...FF,他们不是平等了: 0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 甚至更奇怪的输出

这是预期的行为吗?

p.s*_*w.g 51

JavaScript中的所有数字都由64位浮点数表示(参见规范的§4.3.19).这意味着它可以精确地表示0最多9007199254740992(十六进制值0x20000000000000)的每个整数.任何大于该值的整数(或小于它的负对应值)可能需要四舍五入到最接近的近似值.

注意:

9007199254740992 === 9007199254740993
> true
Run Code Online (Sandbox Code Playgroud)

但是,当您比较它们时,两个舍入到足够不同的近似值的数字仍然会评估为不同的值.例如:

9007199254740992 === 9007199254740994
> false
Run Code Online (Sandbox Code Playgroud)

这是您在第二个片段中看到的,您可以在其中添加另一个F数字.

  • "你在JavaScript中可以完全代表的最大整数是9007199254740992"严格来说,这是不正确的.你想说的是它是*所有整数*可以完全表示的最大数字.但这并不意味着没有更大的整数可以准确表示.事实上,任何大于它的有限双数代表一些整数. (5认同)
  • @AcidShout它不使用128位浮点数,因为语言规范声明它必须使用64位浮点数.([§4.3.19](http://www.ecma-international.org/ecma-262/5.1/#sec-4.3.19)) (4认同)

Lui*_*lli 29

0x100000000000000 == 0xFFFFFFFFFFFFFF给出true同时 0x10000000000000 == 0xFFFFFFFFFFFFF给予false.所以前者是"限制",比方说.

让我们分析数字:0xFFFFFFFFFFFFF内部表示中的52位和一个额外的位为0x10000000000000.

编辑:如此数量的数字不是由长整数表示,而是由双精度浮点数表示.这是因为它们超过了整数值的32位表示, javascript中的每个数字都表示为IEEE754双精度浮点数.

当您在内部表示任何IEEE754 Double Precission FP编号时,您将获得:

0111 1111 1111 2222
2222 2222 2222 2222
2222 2222 2222 2222
2222 2222 2222 2222
Run Code Online (Sandbox Code Playgroud)

其中(0)是符号位,(1)指数位,(2)尾数位.

如果你在JavaScript中进行比较,即使该操作有浮动的不精确(即你得到一些错误),0.5 == 5 * 0.1你也会得到true.所以Javascript容忍浮点运算中的一个小错误,所以像这样的操作给出了真实,正如常识所说的那样.

编辑 -有些东西错了,我写了一篇关于尾数:是的,每个尾数开始用1(据说这样的尾数是标准化的),认为1不存储在一个标准化的数字(每个非零指数只有规范化号码与号码尾数.指数000 0000 0000不遵循这个规则).这意味着每个标准化的尾数都有52个显式位和一个隐式1.

现在:52位怎么样?请注意,0xFF ...的长度为52位.这意味着它将被存储为:0表示符号(它是正数),52表示指数,52"1"数字存储在尾数中(参见本答案底部的最后一个注释).由于一个"1"是隐含的,我们将存储51"1"和一个"0".

0100 0011 0010 1111
1111 1111 1111 1111
1111 1111 1111 1111
1111 1111 1111 1110

(exponent 1075 corresponds to actual exponent 52)
Run Code Online (Sandbox Code Playgroud)

另一个数字有53位:一个"1"和52"0".由于第一个"1"是隐式的,它将被存储为:

0100 0011 0100 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000

(exponent 1076 corresponds to actual exponent 53)
Run Code Online (Sandbox Code Playgroud)

现在是时候比较价值了.他们将在条件相等的情况下进行比较:首先我们对符号进行符号和指数.如果它们相等,我们会考虑尾数.

考虑到作为四舍五入的乘积容忍的小误差,这里进行比较.这样小量是考虑到(ε-约为= 2 ^ -53)和FP ALU检测,比较,这些数字仅在这样的ε-不同,所以他们似乎仅在该上下文中是相等的(存在其中这确实多次不能保存你0.3 == 0.2 + 0.1,因为三个数字中的每一个都是二进制不可表示的,与之相反0.5,并且可以容忍错误0.1 + 0.4.

注意关于尾数和FP表示:从概念上讲,尾数始终低于1.如果要表示更高的数字,则必须使用指数来构思尾数.例子:

  • 0.5表示为0.5 * 2 ^ 0(在数学中考虑正确的运算符优先级).
  • 1表示不是1 * 2 ^ 0因为尾数总是小于1,所以表示将是0.5 * 2 ^ 1.
  • 65,二进制表示为1000001,将存储为(65/128) * 2 ^ 7.

这些数字表示为(记住:第一个"1"是隐式的,因为这些指数用于标准化数字):

0011 1111 1111 0000
... more 0 digits

(exponent 1023 stands for actual exponent 0, mantissa in binary repr. is 0.1, and the first "1" is implicit).

0100 0000 0000 0000
... more 0 digits

(exponent 1024 stands for actual exponent 1, mantissa in binary repr. is 0.1, and the first "1" is implicit).
Run Code Online (Sandbox Code Playgroud)

0100 0000 0110 0000
0100 0000 0000 0000

(exponent 1030 stands for actual exponent 7, mantissa in binary repr. is 0.1000001, and since the first "1" is implicit, it is stored as 0000 0100 0000...)
Run Code Online (Sandbox Code Playgroud)

注意关于指数:通过允许负指数也可以实现更低的精确度:指数似乎是正的 - 未指定 - 但实际情况是你必须减去1023(称为"偏差")到该数字才能得到实际的指数(这意味着指数"1"实际上对应于2 ^( - 1022)).将其转换为基于10的幂,对于十进制数,最低指数为-308(同时考虑后面将要显示的尾数可能).最低正数是:

0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0001
Run Code Online (Sandbox Code Playgroud)

这是:(1 * 2^-52) * 2^-1023由尾数给出的第一个-52和由指数给出的-1023.最后一个是:1*2 ^( - 1075),它总是告诉10 ^ -308.

最低指数000 0000 0000对应于(-1023).有一条规则:每个尾数必须以(隐式)"1"开头或具有此指数.另一方面,最高指数可能是111 1111 1111,但这个指数是为特殊的伪数保留的:

0111 1111 1111 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
Run Code Online (Sandbox Code Playgroud)

对应于+ Infinity,同时:

1111 1111 1111 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
Run Code Online (Sandbox Code Playgroud)

对应于-Infinity,以及具有非零尾数的任何模式,如:

?111 1111 1111 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0001
Run Code Online (Sandbox Code Playgroud)

对应于NaN(不是数字;理想地表示诸如log(-1)或0/0之类的东西).实际上我不确定NaN使用的是哪种尾巴(安静或信号NaN).问号代表任何一点.