NumberLong MongoDB только 54 бит подписан? - PullRequest
3 голосов
/ 15 октября 2011

Является ли NumberLong MongoDB действительно 64-битным целым числом со знаком действительно 64-битным?

Говорят, что NumberLong MongoDB является 64-битным целым числом со знаком, что должно означать, что мы можем играть с -2 ^ 63 <= x <=2 ^ 63-1, где x - это NumberLong.Однако добавление 1 или вычитание 1 из NumberLong (x) не возвращает ожидаемое значение для x <= -2 ^ 54 или x> = 2 ^ 54, но правильные значения возвращаются для -2 ^ 53 <= x <=2 ^ 53. <br>Надежные числа NumberLong для этого кажутся целыми числами со знаком 54 бита.

Почему это так?
Я что-то не так делаю?

Образец из оболочки Монго:

> 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 ]

2 голосов
/ 25 января 2012

Ого, это озадачивает. Очевидно, что здесь происходит какая-то ошибка округления.

> 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

Возможно, это что-то не так с движком 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 }

Определенно хорошо иметь в виду.

0 голосов
/ 16 октября 2011

Math.pow скорее всего работает на double с. Целые числа приводятся к числам с плавающей точкой, и при 2 ^ 54 числа с двойной точностью теряют разрешение на месте 1. Информация уже потеряна во время преобразования обратно в целочисленные типы.

Получаете ли вы сопоставимые результаты, используя сдвиг влево << или повторное умножение?

...