为什么MongoShell中的NumberLong(9007199254740993)与MongoDB中的NumberLong(9007199254740992)匹配?

为什么MongoShell中的NumberLong(9007199254740993)与MongoDB中的NumberLong(9007199254740992)匹配?,mongodb,long-integer,Mongodb,Long Integer,当给定的数字足够大(大于9007199254740992)时就会出现这种情况,再加上更多的测试,我甚至发现许多相邻的数字可以匹配单个数字 不仅NumberLong(9007199254740996)将匹配NumberLong(“9007199254740996”),而且NumberLong(9007199254740995)和NumberLong(9007199254740997) 当我想用一条记录的号码来处理它时,我实际上可以用三个不同的相邻号码来取回同一条记录 人们接受的答案是有道理的,我引

当给定的数字足够大(大于9007199254740992)时就会出现这种情况,再加上更多的测试,我甚至发现许多相邻的数字可以匹配单个数字

不仅
NumberLong(9007199254740996)
将匹配
NumberLong(“9007199254740996”)
,而且
NumberLong(9007199254740995)
NumberLong(9007199254740997)

当我想用一条记录的号码来处理它时,我实际上可以用三个不同的相邻号码来取回同一条记录

人们接受的答案是有道理的,我引用了下面最相关的部分:

警告:不要试图用太大的数字调用构造函数,即不要尝试
db.foo.insert({“t”:NumberLong(1234657890132456789)})
;因为这个数字对于一个double来说太大了,所以它会导致舍入错误。上述数字将转换为
NumberLong(“1234657890132456704”)
,这显然是错误的

以下是一些补充,让事情更清楚:

首先,Mongo shell是一个JavaScript shell。JS不区分整型值和浮点值。JS中的所有数字都表示为浮点值。这意味着mongo shell默认使用64位浮点数。如果shell看到
“9007199254740995”
,它会将其视为字符串,并将其转换为
long
。但当我们省略双引号时,MongoShell将看到未加引号的
9007199254740995
,并将其视为一个浮点数

其次,JS使用IEEE 754标准中定义的64位浮点格式来表示数字,它可以表示的最大值是:

,而最低限额为:

实数的数量是无限的,但在JS浮点格式中只能精确表示有限数量的实数。这意味着在JS中处理实数时,数字的表示通常是实际数字的近似值。

这带来了所谓的舍入误差问题。由于整数也以二进制浮点格式表示,因此尾随数字精度损失的原因实际上与小数相同

JS数字格式允许您精确地表示

这里,由于数字大于9007199254740992,因此肯定会出现舍入错误。
NumberLong(9007199254740995)
NumberLong(9007199254740996)
NumberLong(9007199254740997)
的二进制表示形式相同。因此,当我们以这种方式查询这三个数字时,我们实际上是在问同样的问题。因此,我们将恢复相同的记录


我认为理解这个问题不是JS特有的是很重要的:它会影响任何使用二进制浮点数的编程语言。

您误用了NumberLong构造函数

正确的用法是给它一个字符串参数,如中所述


NumberDecimal
可用于长度大于
NumberLong
的数字。
NumberLong("2090845886852")