Is really a MongoDB's NumberLong really 64 bit signed integer really 64 bit?

MongoDB's NumberLong is stated to become a 64 bit signed integer, that ought to mean we are able to have fun with -2^63 <= x <= 2^63-1, where x is really a NumberLong. However, adding 1 or subtracting 1 from the NumberLong(x) doesn't return the expected value for x <= -2^54 or x >= 2^54, but correct values are came back for -2^53 <= x <= 2^53.
Trustworthy NumberLong amounts because of this appear to become 54 bit signed integers.

Why?
Shall We Be Held doing someting wrong?

Sample in the mongo spend:

> 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

Using MongoDB 2..

Math.pow probably works on doubles. The integers are pushed to floats, and also at 2^54, double precision amounts lose resolution in the 1's place. The details are lost at that time you are converting to integer types.

would you get comparable results using left change << or repeated multiplication?

Wow, this really is puzzling. Clearly there's some kind of rounding error happening here.

> 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

This really is most likely a problem using the JavaScript engine the spend runs in, instead of MongoDB itself. Look at this, for instance--$inc works fine:

> 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") }

You need to be careful, though. If you are using only a normal literal 1, it'll convert the kind to some Number, which in turn breaks the $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 }

And even when you return to $inc with NumberLong("1"), will still be damaged:

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

Certainly good to bear in mind.