2011-10-15 3 views
3

Est-ce qu'un NumberLong est vraiment un nombre entier 64 bits de MongoDB réellement 64 bits?Nombre de MongoDB seulement 54 bits signés?

On dit que NumberLong de MongoDB est un entier signé de 64 bits, ce qui devrait signifier que nous pouvons jouer avec -2^63 < = x < = 2^63-1, où x est un NumberLong. Cependant, l'ajout de 1 ou la soustraction de 1 d'un NumberLong (x) ne renvoie pas la valeur attendue pour x < = -2^54 ou x> = 2^54, mais les valeurs correctes sont renvoyées pour -2^53 < = x < = 2^53.
Les nombres NumberLong fiables à cet effet semblent être des entiers signés à 54 bits.

Pourquoi est-ce?
Est-ce que je fais quelque chose de mal?

échantillon de la coquille mongo:

> 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 

En utilisant MongoDB 2.0.0

Répondre

0

Math.pow fonctionne très probablement sur double s. Les entiers sont contraints à des flottants, et à 2^54, les nombres de double précision perdent la résolution à la place de 1. Les informations sont déjà perdues au moment de la conversion en types entiers.

obtenez-vous des résultats comparables en utilisant le décalage de gauche << ou la multiplication répétée?

+0

Votre réflexion sur la mantisse flottante expliquerait tout cela. Malheureusement, les entiers de décalage gauche ne fonctionnent que pour 32 bits signés et la multiplication répétée donne la même sortie que celle mentionnée ci-dessus. – fsto

2

Wow, c'est énigmatique. De toute évidence, il y a une sorte d'erreur d'arrondi qui se produit ici.

> 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 

Ceci est probablement quelque chose de mal avec le moteur JavaScript dans lequel s'exécute le shell, plutôt que MongoDB lui-même. Check this out, pour exemple-- $inc belles œuvres:

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

Vous devez être prudent, cependant. Si vous utilisez juste un 1 littéral normal, il convertit le type d'un numéro, qui rompt alors le $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 } 

Et même si vous revenez à $inc avec NumberLong("1"), il est encore cassé:

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

Certainement bon à garder à l'esprit.

Questions connexes