While playing around with random numbers in JavaScript I discovered a surprising bug, presumably in the V8 JavaScript engine in Google Chrome. Consider:
// Generate a random number [1,5]. var rand5 = function() { return parseInt(Math.random() * 5) + 1; }; // Return a sample distribution over MAX times. var testRand5 = function(dist, max) { if (!dist) { dist = {}; } if (!max) { max = 5000000; } for (var i=0; i<max; i++) { var r = rand5(); dist[r] = (dist[r] || 0) + 1; } return dist; };
Now when I run testRand5()
I get the following results (of course, differing slightly with each run, you might need to set "max" to a higher value to reveal the bug):
var d = testRand5(); d = { 1: 1002797, 2: 998803, 3: 999541, 4: 1000851, 5: 998007, 10: 1 // XXX: Math.random() returned 4.5?! }
Interestingly, I see comparable results in node.js, leading me to believe it's not specific to Chrome. Sometimes there are different or multiple mystery values (7, 9, etc).
Can anyone explain why I might be getting the results I see? I'm guessing it has something to do with using parseInt
(instead of Math.floor()
) but I'm still not sure why it could happen.
Math.random() The Math.random() function returns a floating-point, pseudo-random number that's greater than or equal to 0 and less than 1, with approximately uniform distribution over that range — which you can then scale to your desired range.
The Math. random() method returns a random number from 0 (inclusive) up to but not including 1 (exclusive).
A random number generator always returns a value between 0 and 1, but never equal to one or the other. Any number times a randomly generated value will always equal to less than that number, never more, and never equal.
random() function: The Math. random() function is used to return a floating-point pseudo-random number between range [0,1) , 0 (inclusive) and 1 (exclusive).
The edge case occurs when you happen to generate a very small number, expressed with an exponent, like this for example 9.546056389808655e-8
.
Combined with parseInt
, which interprets the argument as a string, hell breaks loose. And as suggested before me, it can be solved using Math.floor
.
Try it yourself with this piece of code:
var test = 9.546056389808655e-8; console.log(test); // prints 9.546056389808655e-8 console.log(parseInt(test)); // prints 9 - oh noes! console.log(Math.floor(test)) // prints 0 - this is better
Of course, it's a parseInt()
gotcha. It converts its argument to a string first, and that can force scientific notation which will cause parseInt to do something like this:
var x = 0.000000004; (x).toString(); // => "4e-9" parseInt(x); // => 4
Silly me...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With