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

时间:2014-08-07 19:25:10

标签: javascript numbers

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

0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

令人惊讶的是,他们是平等的: Strange output

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

如果我向F添加0xFFFF...FF,它们将不再相同: 0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF Even stranger output

这是预期的行为吗?

4 个答案:

答案 0 :(得分:51)

JavaScript中的所有数字都由64-bit floating point numbers内部表示(请参阅规范的§4.3.19)。这意味着它可以精确地表示从09007199254740992(十六进制值0x20000000000000)的每个整数。任何大于该整数(或小于它的负对应物)的整数可能需要四舍五入到最接近的近似值。

观察:

9007199254740992 === 9007199254740993
> true

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

9007199254740992 === 9007199254740994
> false

这是您在添加另一个F数字的第二个代码段中看到的内容。

答案 1 :(得分:29)

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

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

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

当您在内部代表任何IEEE754 Double Precission FP Number时,您会得到:

0111 1111 1111 2222
2222 2222 2222 2222
2222 2222 2222 2222
2222 2222 2222 2222

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

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

编辑 - 我写了关于尾数的错误:是的,每个尾数都以1开头(据说这样的尾数标准化), 1未存储在规范化数字中(每个非零指数只有规范化数字。对于带有指数{{1}的数字,mantissas不遵守这条规则)。这意味着每个标准化的尾数都有52个显式位,而隐式1

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

000 0000 0000

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

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

(exponent 1075 corresponds to actual exponent 52)

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

这里的比较是考虑到作为四舍五入的乘积容忍的小误差。考虑到这样的 epsilon (epsilon约为= 2 ^ -53),FP ALU检测到相对地,这些数字仅在这样的epsilon中有所不同,因此它们似乎只在该上下文中相等(很多时候,这不会像0100 0011 0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 (exponent 1076 corresponds to actual exponent 53) 那样拯救你,三个数字中的每一个都是二进制不可表示的,与0.3 == 0.2 + 0.1形成鲜明对比,0.5可以容忍0.1 + 0.4的错误。 1}})。

注意关于尾数和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).

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...)

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

0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0001

是:(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

对应于+ Infinity,而:

1111 1111 1111 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000

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

?111 1111 1111 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0001

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

答案 2 :(得分:4)

以下十六进制号码:

0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

存储为IEEE 754标准浮点值:

1.3407807929942597e+154

您为此号码添加1,它变为:

0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

存储为:

1.3407807929942597e+154

这两个数字都在数字范围之外,可以通过JavaScript编号(ref)准确表示。在上面的例子中,两个数字最终都具有相同的内部表示,因此它们是相等的。

提醒:不应使用等号运算符(ref)来比较浮点数。

答案 3 :(得分:2)

这显然是溢出或舍入。以数学方式计算出数字的大小,并检查最大数字。