简体   繁体   English

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

[英]Why are two different numbers equal in JavaScript?

I've been messing around with a JavaScript console, when I suddenly decided to try this:我一直在搞一个 JavaScript 控制台,当我突然决定尝试这个时:

0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

Surprisingly, they're equal:令人惊讶的是,它们是相等的:奇怪的输出

Why does it happen?为什么会发生? They're clearly different numbers (even the 0xFFFF...FFFF is one digit shorter)它们显然是不同的数字(即使是0xFFFF...FFFF也短一位数)

If I add a F to the 0xFFFF...FF , they're not equal anymore: 0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF如果我添加一个F0xFFFF...FF ,他们不是平等了: 0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF更陌生的输出

Is this expected behaviour?这是预期的行为吗?

All numbers in JavaScript are internally represented by 64-bit floating point numbers (see §4.3.19 of the specification). JavaScript 中的所有数字都在内部由64 位浮点数表示(参见规范的§4.3.19 )。 That means it can exactly represent every integer from 0 up to 9007199254740992 (hex value 0x20000000000000 ).这意味着它可以准确地表示从09007199254740992 (十六进制值0x20000000000000 )的每个整数。 Any integers greater than that (or less than it's negative counterpart) may need to be rounded to the closest approximate value.任何大于(或小于其负对应值)的整数可能需要四舍五入到最接近的近似值。

Observe:观察:

9007199254740992 === 9007199254740993
> true

However, two numbers that are rounded to sufficiently different approximate values still evaluate to different values when you compare them.但是,四舍五入为足够不同的近似值的两个数字在比较它们时仍会评估为不同的值。 For example:例如:

9007199254740992 === 9007199254740994
> false

This is what you're seeing in the second snippet where you add another F digit.这就是您在添加另一个F数字的第二个代码段中看到的内容。


Note: The ECMAScript specification now define Number.MAX_SAFE_INTEGER as a global constant equal to 9007199254740991 .注意: ECMAScript 规范现在将Number.MAX_SAFE_INTEGER定义为等于9007199254740991的全局常量。

0x100000000000000 == 0xFFFFFFFFFFFFFF gives true while 0x10000000000000 == 0xFFFFFFFFFFFFF gives false . 0x100000000000000 == 0xFFFFFFFFFFFFFF给出true0x10000000000000 == 0xFFFFFFFFFFFFF给出false So the former is the "limit", say.所以前者是“极限”,比如说。

Let's analyze the numbers: 52 bits for 0xFFFFFFFFFFFFF and one additional bit for 0x10000000000000 in the internal representation.让我们分析数字:在内部表示中,52 位表示0xFFFFFFFFFFFFF ,另外 1 位表示 0x10000000000000。

EDIT : Numbers of such magnitude are not represented by long ints but by double precission floats.编辑:这种量级的数字不是由长整数表示,而是由双精度浮点数表示。 This is because they exceed the 32bit representation of an integer value every number in javascript is represented as IEEE754 double precision floating point.这是因为它们超过了整数值的 32 位表示, javascript 中的每个数字都表示为 IEEE754 双精度浮点数。

When you represent any IEEE754 Double Precission FP Number internally, you get:当您在内部表示任何IEEE754 双精度 FP 数时,您将得到:

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

Where (0) is the sign bit, (1) the exponent bits, and (2) the mantissa bits.其中 (0) 是符号位,(1) 指数位,以及 (2) 尾数位。

If you compare in JavaScript 0.5 == 5 * 0.1 you will get true even when that operation has floating imprecission (ie you get some error).如果您在 JavaScript 0.5 == 5 * 0.1进行比较,即使该操作具有浮动不精确性(即您得到一些错误),您也会得到true So Javascript tolerates a small error in floating point operations so operations like that give true, as the common sense tells.所以 Javascript 可以容忍浮点运算中的小错误,所以像常识一样,这样的运算是正确的。

Edit - There's something wrong I wrote about the Mantissa: Yes, every Mantissa starts with 1 (it is said that such mantissa is normalized ), BUT that 1 is not stored in a normalized number (each nonzero exponent has only normalized numbers. mantissas for numbers with exponent 000 0000 0000 do not follow this rule).编辑- 我写的尾数有问题:是的,每个尾数都以1开头(据说这样的尾数是标准化的),但是1没有存储在标准化数中(每个非零指数只有标准化数。尾数为指数为000 0000 0000数字不遵循此规则)。 This means that every normalized mantissa has 52 explicit bits, and an implicit 1 .这意味着每个标准化尾数都有 52 个显式位和一个隐式 1

Now: what about the 52 bits?现在:那 52 位呢? Notice that the 0xFF... has 52 bits length.请注意, 0xFF... 有 52 位长度。 This means that it will be stored as: 0 for the sign (it is positive), 52 for the exponent, and 52 "1" digits in the mantissa (see a final note at the foot of this answer).这意味着它将被存储为:0 表示符号(它是正数),52 表示指数,以及尾数中的 52 个“1”数字(请参阅本答案底部的最后一个注释)。 Since one "1" is implicit, we'll store 51 "1" and one "0".由于一个“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)

AND the other number has 53 bits: one "1" and 52 "0".另一个数字有 53 位:一个“1”和 52 个“0”。 Since the first "1" is implicit, it will be stored like:由于第一个“1”是隐式的,它将被存储为:

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

(exponent 1076 corresponds to actual exponent 53)

Now it's time to compare values.现在是比较值的时候了。 They will compare in equality of conditions : first we take sign and exponent to the comparisson.他们将在条件相等的情况下进行比较:首先我们将符号和指数用于比较。 If they're equal, we consider the mantissa.如果它们相等,我们考虑尾数。

Comparisson here takes place considering a small error being tolerated as a product of rounding.考虑到作为舍入乘积的小误差,这里进行比较。 Such epsilon is taken into account (epsilon is about =2^-53) and FP ALU detects that, relatively, those numbers differ only in such epsilon, so they seem to be equal only in that context (there are many times where this does not save you as in cases of 0.3 == 0.2 + 0.1 , being each of three numbers binary-non-representable, in contrast to 0.5 which is, and can tolerate an error against 0.1 + 0.4 ).这样的epsilon被考虑在内(epsilon 约为 =2^-53)并且 FP ALU 检测到,相对而言,这些数字仅在此类 epsilon 中不同,因此它们似乎仅在该上下文中相等(有很多次这样的情况)在0.3 == 0.2 + 0.1情况下,不会保存您,因为三个数字中的每一个都是二进制不可表示的,而0.5是,并且可以容忍针对0.1 + 0.4的错误)。

Note About the mantissa and the FP representation: The Mantissa is always, conceptually, lower than 1. If you want to represent a higher number, you must conceive it using an exponent.关于尾数和 FP 表示的注意事项:尾数在概念上始终小于 1。如果您想表示更大的数字,则必须使用指数来构思它。 Examples:例子:

  • 0.5 is represented as 0.5 * 2 ^ 0 (consider the right operator precedence in math). 0.5 表示为0.5 * 2 ^ 0 (考虑数学中正确的运算符优先级)。
  • 1 is represented not as 1 * 2 ^ 0 since the mantissa is always lower than 1, so the representation will be 0.5 * 2 ^ 1 . 1 不表示为1 * 2 ^ 0因为尾数总是小于 1,所以表示为0.5 * 2 ^ 1
  • 65, which has binary representation as 1000001, will be stored as (65/128) * 2 ^ 7 . 65 的二进制表示为 1000001,将存储为(65/128) * 2 ^ 7

These numbers are represented as (remember: the first "1" is implicit since these exponents are for normalized numbers):这些数字表示为(请记住:第一个“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).

and

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

Note About the exponent: Lower precission can be achieved by allowing negative exponents as well: Exponents seem positive -unsigned- but the reality is that you must subtract 1023 (called "bias") to that number to get the actual exponent (this means that exponent "1" actually corresponds to 2^(-1022)).关于指数的注意事项:也可以通过允许负指数来实现较低的精度:指数看起来是正数 - 无符号 - 但实际情况是您必须将该数字减去 1023(称为“偏差”)才能获得实际指数(这意味着指数“1”实际上对应于 2^(-1022))。 Translating this to a 10-based power, the lowest exponent is -308 for decimal numbers (considering also the mantissa possition as I will show later).将其转换为基于 10 的幂,十进制数的最低指数为 -308(还要考虑尾数位置,我将在后面展示)。 The lowest positive number is:最小的正数是:

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

which is: (1 * 2^-52) * 2^-1023 being the first -52 given by the mantissa and the -1023 by the exponent.即: (1 * 2^-52) * 2^-1023是尾数给出的第一个 -52 和指数给出的 -1023。 The final one is: 1 * 2^(-1075), which goes towards the 10^-308 always told.最后一个是:1 * 2^(-1075),它趋向于总是被告知的 10^-308。

The lowest exponent is 000 0000 0000 corresponding to (-1023).最低指数是000 0000 0000对应于 (-1023)。 There's a rule: Every mantissa must begin with (an implicit) "1" or have this exponent.有一条规则:每个尾数都必须以(隐含的)“1”开头或具有该指数。 On the other side, the highest exponent could be 111 1111 1111 , but this exponent is reserved for special pseudonumbers:另一方面,最高指数可能是111 1111 1111 ,但这个指数是为特殊伪数保留的:

0111 1111 1111 0000
0000 0000 0000 0000
0000 0000 0000 0000
0000 0000 0000 0000

corresponds to +Infinity, while:对应于 +Infinity,而:

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

corresponds to -Infinity, and any pattern with a nonzero mantissa, like:对应于 -Infinity,以及具有非零尾数的任何模式,例如:

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

correspond to NaN (not a number; ideal to represent stuff like log(-1) or 0/0).对应于 NaN(不是数字;非常适合表示 log(-1) 或 0/0 之类的东西)。 Actually I'm not sure what mantissas are used for NaN (either quiet or signaling NaN).实际上,我不确定 NaN 使用什么尾数(安静或信号 NaN)。 The question mark stands for any bit.问号代表任何位。

The following hexa decimal number:以下十六进制数:

0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

is stored as the IEEE 754 standard float value:存储为 IEEE 754 标准浮点值:

1.3407807929942597e+154

You add 1 to this number and it becomes:你在这个数字上加 1,它变成:

0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

which is also stored as:存储为:

1.3407807929942597e+154

Both numbers are lie outside the range of numbers that can be accurately represented by the JavaScript Number ( ref ).这两个数字都位于 JavaScript 数字 ( ref ) 可以准确表示的数字范围之外。 In the above example, both numbers end up having the same internal representation, hence they are, sort of, equal.在上面的例子中,两个数字最终具有相同的内部表示,因此它们在某种程度上是相等的。

Reminder: one should not compare floating point numbers using the equality operator ( ref ).提醒:不应使用相等运算符 ( ref ) 比较浮点数。

This is obviously overflow or rounding.这显然是溢出或舍入。 Work out mathematically the magnitude of the numbers and check against the largest number.用数学方法计算出数字的大小并检查最大的数字。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM