読者です 読者をやめる 読者になる 読者になる

中年engineerの独り言 - crumbjp

LinuxとApacheの憂鬱

MongoDBの64bit整数型が思ったより小さかった

mongodb

ご存じの様に、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 の問題の可能性もある。