40 votes

Math.random () renvoie une valeur supérieure à un?

Tout en jouant avec des nombres aléatoires en JavaScript, j'ai découvert un bug surprenant, sans doute dans le moteur JavaScript V8 de Google Chrome. Considérer:

// 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;
};

Maintenant quand je le lance en testRand5() - je obtenir les résultats suivants (bien sûr, légèrement différentes à chaque exécution, vous pourriez avoir besoin pour définir "max" pour une valeur plus élevée pour révéler le bug):

var d = testRand5();
d = {
  1: 1002797,
  2: 998803,
  3: 999541,
  4: 1000851,
  5: 998007,
  10: 1 // XXX: Math.random() returned 4.5?!
}

Il est intéressant de noter, je vois des résultats comparables dans node.js, m'amenant à croire que c'est pas spécifique à google Chrome. Parfois, il y a différentes ou plusieurs mystère valeurs (7, 9, etc).

Quelqu'un peut-il expliquer pourquoi je suis peut-être obtenir les résultats que je vois? J'imagine qu'il a quelque chose à faire avec l'aide d' parseInt (au lieu de Math.floor()) mais je ne suis toujours pas sûr de savoir pourquoi cela pourrait se produire.

76voto

Jakob Points 11155

Le cas limite se produit lorsque vous générez un très petit nombre, exprimé avec un exposant, comme par exemple 9.546056389808655e-8 .

Combiné à parseInt , qui interprète l'argument comme une chaîne , l'enfer se déchaîne. Et comme suggéré précédemment, cela peut être résolu en utilisant Math.floor .

Essayez vous-même avec ce morceau de 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
 

38voto

maerics Points 47743

Bien sûr, c'est un parseInt() obtenu. Il convertit d'abord son argument en chaîne , ce qui peut imposer une notation scientifique qui fera que parseInt fera quelque chose comme ceci:

 var x = 0.000000004;
(x).toString(); // => "4e-9"
parseInt(x); // => 4
 

Que je suis bête...

10voto

jfriend00 Points 152127

Je voudrais suggérer de changer votre nombre aléatoire de fonction de ceci:

var rand5 = function() {
  return(Math.floor(Math.random() * 5) + 1);
};

Cela permettra de générer de manière fiable une valeur entière entre 1 et 5 inclus.

Vous pouvez voir votre fonction de test en action ici: http://jsfiddle.net/jfriend00/FCzjF/.

Dans ce cas, parseInt n'est pas le meilleur choix, car il va convertir votre flotteur à une chaîne de caractères qui peut être un certain nombre de différents formats (y compris la notation scientifique) et ensuite essayer d'analyser un nombre entier hors de lui. Beaucoup mieux de simplement fonctionner sur le flotteur directement avec Math.floor().

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X