MongoDBの64bit整数型が思ったより小さかった
ご存じの様に、MongoDBの数値型(number)はfloat(64-bit) だ。
number ( 64-bit float) : double
- 指数部
- 52 bit(精度は53 bit)
- 仮数部
- 11 bit
- 符号
- 1 bit
なので0x20000000000001=2^53+1は桁溢れを起す。
> db.test.drop()
true
> db.test.save({i:0x1fffffffffffff})
> db.test.save({i:0x20000000000000})
> db.test.save({i:0x20000000000001})
> db.test.find({},{_id:0})
{ "i" : 9007199254740991 }
{ "i" : 9007199254740992 }
{ "i" : 9007199254740992 }
NumberLong (64-bit integer) : int64_t ?
問題はコイツだ。
マニュアルには単に64-bit integerとしか書かれて無い。
しかし・・・
> db.test.drop()
true
> db.test.save({i:NumberLong(0x20000000000001)})
> db.test.find({},{_id:0})
{ "i" : NumberLong("9007199254740992") }おいおい、、53-bitで溢れてるじゃねーか!!
しかも
- 本来の64bit integer最大値0x7fffffffffffffffはマイナスで
- 少し小さいは0x7000000000000000はプラス!
- じゃあ符号ビットが違う所にあるのかと思いきやそうでもない・・・
> db.test.save({i:NumberLong(0x7fffffffffffffff)})
> db.test.save({i:NumberLong(0x7000000000000000)})
> db.test.save({i:NumberLong(0x6fffffffffffffff)})
> db.test.find({},{_id:0})
{ "i" : NumberLong("-9223372036854775808") }
{ "i" : NumberLong("8070450532247928832") }
{ "i" : NumberLong("8070450532247928832") }多分途中の処理のどこかでdoubleにしちゃってるんだろうな・・・
あとMongoDBの問題じゃなくてMongo shell の問題の可能性もある。