javascript - Math.random() devuelve un valor mayor que uno
google-chrome v8 (3)
Mientras jugaba con números aleatorios en JavaScript, descubrí un error sorprendente, presumiblemente en el motor de JavaScript V8 en Google Chrome. Considerar:
// 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;
};
Ahora cuando ejecuto testRand5()
obtengo los siguientes resultados (por supuesto, difiriendo ligeramente con cada ejecución, es posible que deba establecer "max" a un valor más alto para revelar el error):
var d = testRand5();
d = {
1: 1002797,
2: 998803,
3: 999541,
4: 1000851,
5: 998007,
10: 1 // XXX: Math.random() returned 4.5?!
}
Curiosamente, veo resultados comparables en node.js, lo que me lleva a pensar que no es específico de Chrome. A veces hay valores misteriosos diferentes o múltiples (7, 9, etc.).
¿Alguien puede explicar por qué puedo obtener los resultados que veo? Supongo que tiene algo que ver con el uso de parseInt
(en lugar de Math.floor()
), pero todavía no estoy seguro de por qué podría suceder.
El caso límite ocurre cuando se genera un número muy pequeño, expresado con un exponente, como este, por ejemplo, 9.546056389808655e-8
.
Combinado con parseInt
, que interpreta el argumento como una cadena , el infierno se desata. Y como se sugirió antes que yo, se puede resolver usando Math.floor
.
Pruébelo usted mismo con este código:
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
Por supuesto, es un parseInt()
gotcha. Primero convierte su argumento en una cadena , y eso puede forzar una notación científica que hará que parseInt haga algo como esto:
var x = 0.000000004;
(x).toString(); // => "4e-9"
parseInt(x); // => 4
Tonto de mí...
Sugeriría cambiar su función de número aleatorio a esto:
var rand5 = function() {
return(Math.floor(Math.random() * 5) + 1);
};
Esto generará de manera confiable un valor entero entre 1 y 5 inclusive.
Puede ver su función de prueba en acción aquí: http://jsfiddle.net/jfriend00/FCzjF/ .
En este caso, parseInt
no es la mejor opción porque convertirá tu float en una cadena que puede ser de diferentes formatos (incluida la notación científica) y luego intentará analizar un entero a partir de ella. Es mucho mejor simplemente operar en el flotador directamente con Math.floor()
.