[SERVER-5045] Illegal math operations computing in JS V8 Created: 22/Feb/12  Updated: 15/Aug/12  Resolved: 15/Mar/12

Status: Closed
Project: Core Server
Component/s: JavaScript
Affects Version/s: 2.1.0
Fix Version/s: None

Type: Bug Priority: Critical - P2
Reporter: Vitaly qxFusion Assignee: Antoine Girbal
Resolution: Done Votes: 0
Labels: V8, javascript, logical, math
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

FC15 x86-64


Operating System: Linux
Participants:

 Description   

For Intensives findAndModify operations which use a service call $inc in some cases there is a loss 0.00000000000000005, 0.00000000000000001, 0.66666666666666667 and 0.33333333333333333. This effect is mistery! No memory leak detected, memory and CPU working correctly. Test also working on other OS.

example for node.js (db already connected for database)

begin:
db.collection("test").save(

{a: 1, test:10000000}

);

test:
for(var i=0;i<1000000;i++) {
db.collection("test").findAndModify(

{a:1}

,[], {$inc: {test: -0.01}},

{new:true}

,function(err,v)

{if(v!=null)console.log(v);}

);
}

After approximately 10 to 10000 operations - there is a loss on the error
This is math error, maybe in V8 engine
P.S. SpiderMonkey engine don't have this bug - I'm already test it.



 Comments   
Comment by Antoine Girbal [ 15/Mar/12 ]

seems like an earlier math v8 bug was fixed

Comment by Antoine Girbal [ 15/Mar/12 ]

Testing above example from Christian, I am seeing same output in v8 and SM when using a recent v8 build.
Potentially they've fix some math since

V8:
> var k = 1000000;
> for(var i = 0; i < 1000; i++) { k = k - 0.01;}
999989.9999999907
 
SM:
> var k = 1000000;
> for(var i = 0; i < 1000; i++) { k = k - 0.01;}
999989.9999999907

Same thing with 1st example

V8:
> db.testjs.drop()
true
> db.testjs.save({a: 1, test:new NumberLong(10000000)});
> for(var i=0;i<10000;i++) { db.testjs.update({a:1}, {$inc: {test: 0.01}} ); }
> db.testjs.find()
{ "_id" : ObjectId("4f61368e8116b302fdf4f577"), "a" : 1, "test" : 10000099.999997765 }
 
SM:
> db.testjs.drop()
true
> db.testjs.save({a: 1, test:new NumberLong(10000000)});
> for(var i=0;i<10000;i++) { db.testjs.update({a:1}, {$inc: {test: 0.01}} ); }
> db.testjs.find()
{ "_id" : ObjectId("4f613dc4df3e6fe969a4644c"), "a" : 1, "test" : 10000099.999997765 }

To have perfect accuracy you would need to do math with no floating points.

Comment by Vitaly qxFusion [ 25/Feb/12 ]

Unfortunatly node.js driver don't have this feature. I'm using number only in 0-10000000 dimension whith 2digit accuracy. I'm think this is problem in JS (ะก++ don't have this problem)

Comment by Antoine Girbal [ 25/Feb/12 ]

it is important to understand that JS uses a double to represent numbers.
Hence some numbers can end up not exact:

  • integers above 2^32 may be inaccurate, and above 2^53 usually wrong
  • floats may not be represented accurately as in your examples.

As christian outlined, the best approach is to only deal with integers if you want perfect accuracy from JS.
You can decide that you numbers all have 2 decimals and then always divide by 100 if you want the approx real value.
Also note that Mongo can store Long values, which are represented as NumberLong() object in the shell (not sure how the nodejs driver handles it).

Comment by Christian Amor Kvalheim [ 25/Feb/12 ]

I'm not sure how they operate on taking tickets but you can ask here

https://groups.google.com/forum/?fromgroups#!forum/v8-users

or

https://groups.google.com/forum/?fromgroups#!forum/v8-dev

I'm not even sure if the behavior is well defined in the js spec so I don't know if spidermonkey or v8 are doing it right or wrong.

Comment by Vitaly qxFusion [ 24/Feb/12 ]

What do you think is possible to make patches for the V8 to optimize the math?
Unfortunately, your proposal will not be used because code is associated with another node.js application (using float model), and correct it takes a VERY LONG time

Thanks for the solution

Comment by Christian Amor Kvalheim [ 24/Feb/12 ]

This is a typical issue with doing math on js due to the usage of floats for all the math. I'm not sure there is an easy way around it or why V8 is behaving differently in this case. My choosen way of doing it would be to switch to fixed integer math with longs (new Long()).

so if you have

var a = 100

and the step = 0.01

then save a as 100 * 100

and scale the step to 1 and back when needed.

f.ex

var k = 1000000;
for(var i = 0; i < 1000; i++)

{ k = k - 0.01;}

equals

999989.9999999907 on spidermonkey
989999.9999906868 on v8 on the lastest 0.6.X node

Comment by Vitaly qxFusion [ 23/Feb/12 ]

Test system is
HV: Oracle xVM VirtualBox Container x86_64
CPU: Intel Core i3
RAM: Guest - 1.5GB (+1GB SWAP) / Host - 8GB
Drive: 2x20GB SAS RAID (Virtual LSI Logic)
OS: Fedora Core 15 (official) + latest kernel (x86_64)

Comment by Vitaly qxFusion [ 23/Feb/12 ]

for scotthernandez - it's bug versions

Comment by Eliot Horowitz (Inactive) [ 23/Feb/12 ]

Can you send the data or a script that fully reproduces?

Comment by Scott Hernandez (Inactive) [ 23/Feb/12 ]

Is there a bug, or did it work?

Comment by Vitaly qxFusion [ 22/Feb/12 ]

This bug work on mongodb 2.0.2-8.fc15(x86_64), mongodb-10gen.x86_64-2.0.2 and also on my custom build
Node.js version 0.3.2-8.fc15, mongo driver from npm 0.9.9-2 (for x86_64) and also on custom built too.

Comment by Scott Hernandez (Inactive) [ 22/Feb/12 ]

Which package(s) didn't work?

Comment by Vitaly qxFusion [ 22/Feb/12 ]

This bug work on 10gen repo version and on manual build too.

Generated at Thu Feb 08 03:07:44 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.