我认为 JavaScript 的松散相等运算符很好,让我可以将 Numbers 与 BigInts 进行比较:
42 == 42n // true!
Run Code Online (Sandbox Code Playgroud)
所以我尝试了一个大于Number.MAX_SAFE_INTEGER. 我认为由于数字会自动四舍五入,因此我可能还必须四舍五入 BigInt 才能将它们视为相等:
9999999999999999 == 10000000000000000 // true - rounded to float64
9999999999999999 == 9999999999999999n // false - makes sense!
10000000000000000 == 9999999999999999n // false - makes sense!
9999999999999999 == 10000000000000000n // true - makes sense!
Run Code Online (Sandbox Code Playgroud)
太好了,有道理——所以我尝试了另一个四舍五入的大数字:
18446744073709551616 == 18446744073709552000 // true - rounded to float64
18446744073709551616 == 18446744073709551616n // true?!
18446744073709552000 == 18446744073709551616n // true?!
18446744073709551616 == 18446744073709552000n // false?!
Run Code Online (Sandbox Code Playgroud)
我在 Chrome、Safari 和 Node.js 中观察到了相同的结果。
为什么这种行为不一致?是不是因为数字被比较为数学值,这意味着什么?
Ber*_*rgi 21
不一致是因为未指定Number::toString抽象操作。
你的问题归结为BigInt(String(x))?BigInt(x),这可能被假定为整数的相等,x但实际上并非如此。
在您的特定情况下,对于x=18446744073709551616或x=18446744073709552000(或介于两者之间,甚至有点左右),数字的字符串表示会产生'18446744073709552000'而确切的数学值是 18446744073709551616。(我们知道这一点是因为NumberToBigInt操作是精确的 - 它让您获得数字的数学值,如果不是整数则出错)。
我们还发现以下注释Number.prototype.toFixed:
的输出
toFixed可能比toString某些值更精确,因为toString只打印足够的有效数字以将数字与相邻的数字值区分开来。例如,
(1000000000000000128).toString()返回"1000000000000000100",而
(1000000000000000128).toFixed(0)返回"1000000000000000128"。
回答标题问题
为什么 Number(“x”) == BigInt(“x”) ... 只是有时?
这是因为浮点数值的精度有限。有多个数字文字被解析为完全相同的数字。与您的第一个示例类似,让我们采用 bigint20000000000000000n。有一个具有相同数学值的浮点数,具体来说
+1× 0b10001110000110111100100110111111000001× 2
= 1 × 152587890625 × 2 170b10001
= 20000000000000000
有迹象表明,评估这个号码值的多个整数文字:19999999999999998,19999999999999999,20000000000000000,20000000000000001,和20000000000000002。(请注意,它并不总是四舍五入)。当您将这些作为字符串并使用一元+或Number对它们。
但是,如果您使用具有相同文本表示的相应 bigint 文字,它们将评估为 5 个不同的BigInt 值。只有一个这些都会比较相等(含==)到,即具有相同的数学值,该数值。
这是一致的:总是只有一个,而且它可以精确地表示为一个浮点数。
为什么这种行为不一致?
您的困惑来自 Number 值 18446744073709551616 的字符串表示。当打印数字文字18446744073709551616, 或 时+'18446744073709551616',您得到18446744073709552000(因为控制台在内部使用String()/ .toString()),这让您假设 18446744073709552000 是它的数学值,并且18446744073709552000n应该比较等于它。但这不是:-/
console.log(18446744073709551616..toString());
console.log(18446744073709551616..toFixed());
console.log(18446744073709552000..toString());
console.log(18446744073709552000..toFixed());Run Code Online (Sandbox Code Playgroud)
该相信哪个?