MongoDB的NumberLong只有54位签名?

时间:2011-10-15 11:23:05

标签: mongodb numbers integer long-integer database

MongoDB的NumberLong真的是64位有符号整数吗64位?

MongoDB的NumberLong被称为64位有符号整数,这意味着我们可以使用-2 ^ 63&lt; = x&lt; = 2 ^ 63-1,其中x是NumberLong。 但是,从NumberLong(x)中加1或减1不会返回x&lt; = -2 ^ 54或x&gt; = 2 ^ 54的预期值,但会返回正确的值-2 ^ 53&lt; = x <= 2 ^ 53 可靠的NumberLong数字似乎是54位有符号整数。

为什么会这样?
我做错了什么?

来自mongo shell的示例:

> NumberLong( Math.pow(2,54) )
NumberLong("18014398509481984")    // Expected
> NumberLong( Math.pow(2,54)-1 )
NumberLong("18014398509481984")    // **NOT** Expected
> NumberLong( -Math.pow(2,54) )
NumberLong("-18014398509481984")   // Expected
> NumberLong( -Math.pow(2,54)+1 )
NumberLong("-18014398509481984")   // **NOT** Expected

> NumberLong( Math.pow(2,53) )
NumberLong("9007199254740992")     // Expected
> NumberLong( Math.pow(2,53)-1 )
NumberLong("9007199254740991")     // Expected
> NumberLong( -Math.pow(2,53) )
NumberLong("-9007199254740992")    // Expected
> NumberLong( -Math.pow(2,53)+1 )
NumberLong("-9007199254740991")    // Expected

使用MongoDB 2.0.0

2 个答案:

答案 0 :(得分:2)

哇,这很令人费解。很明显,这里发生了某种舍入错误。

> NumberLong("18014398509481984")-NumberLong("1");
18014398509481984

> NumberLong("18014398509481984")-NumberLong("2");
18014398509481982

> NumberLong("18014398509481984")+NumberLong("1");
18014398509481984

> NumberLong("18014398509481984")+NumberLong("2");
18014398509481984

> NumberLong("18014398509481984")+NumberLong("3");
18014398509481988

这可能是shell运行的JavaScript引擎的错误,而不是MongoDB本身。例如,检查一下 - $inc正常工作:

> db.test.insert({twoTo54:NumberLong("18014398509481984")});
> db.test.update({},{$inc:{twoTo54:NumberLong("1")}});
> db.test.find();
{ "_id" : ObjectId("4f204847756aa806028abce1"), "twoTo54" : NumberLong("18014398509481985") }
> db.test.update({},{$inc:{twoTo54:NumberLong("1")}});
> db.test.find();
{ "_id" : ObjectId("4f204847756aa806028abce1"), "twoTo54" : NumberLong("18014398509481986") }
> db.test.update({},{$inc:{twoTo54:NumberLong("1")}});
> db.test.find();
{ "_id" : ObjectId("4f204847756aa806028abce1"), "twoTo54" : NumberLong("18014398509481987") }
但是,你必须要小心。如果您只使用普通文字1,它会将类型转换为数字,然后打破$inc

> db.test.update({},{$inc:{twoTo54:1}});
> db.test.find();
{ "_id" : ObjectId("4f204847756aa806028abce1"), "twoTo54" : 18014398509481988 }
> db.test.update({},{$inc:{twoTo54:1}});
> db.test.find();
{ "_id" : ObjectId("4f204847756aa806028abce1"), "twoTo54" : 18014398509481988 }

即使您使用$inc返回NumberLong("1"),它仍然会被破坏:

> db.test.update({},{$inc:{twoTo54:NumberLong("1")}});
> db.test.find();
{ "_id" : ObjectId("4f204847756aa806028abce1"), "twoTo54" : 18014398509481988 }

绝对要记住。

答案 1 :(得分:0)

Math.pow最有可能在double上运行。整数被强制浮动,并且在2 ^ 54,双精度数字在1的位置失去分辨率。在您转换回整数类型时,信息已经丢失。

您是使用左移<<还是重复乘法获得可比较的结果?