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

en jouant avec des nombres aléatoires en JavaScript, j'ai découvert un bug surprenant, probablement 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 lance testRand5() j'obtiens les résultats suivants (Bien sûr, différant légèrement à chaque exécution, vous pourriez avoir besoin de mettre "max" à 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?!
}

fait intéressant, je vois des résultats comparables dans le noeud.js, ce qui me fait croire que ce N'est pas spécifique à Chrome. Parfois il y a des valeurs mystérieuses différentes ou multiples (7, 9, etc.).

quelqu'un peut-il expliquer pourquoi je pourrais obtenir les résultats que je vois? Je suppose que cela a quelque chose à voir avec l'utilisation parseInt (au lieu de Math.floor()) mais je ne sais toujours pas pourquoi cela pourrait se produire.

40
demandé sur Josh Lee 2011-09-08 23:46:08

3 réponses

le cas de bord se produit quand vous générez un nombre très petit, exprimé avec un exposant, comme ceci par exemple 9.546056389808655e-8.

combiné avec parseIntinterprète l'argument comme une chaîne de caractères, l'enfer se déchaîne. Et comme suggéré avant moi, il peut être résolu en utilisant Math.floor.

Essayez-le 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
75
répondu Jakob 2011-09-14 17:52:20

bien sûr, c'est un parseInt() gotcha. Il convertit son argument en chaîne tout d'abord, et cela peut forcer la notation scientifique qui poussera parseInt à faire quelque chose comme ceci:

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

je suis Bête...

37
répondu maerics 2011-09-08 19:54:40

je suggère de changer votre fonction de nombre aléatoire en ceci:

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

cela générera de façon fiable une valeur entière comprise entre 1 et 5 inclusivement.

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 en une chaîne qui peut être un certain nombre de formats différents (y compris la notation scientifique) et ensuite essayer de analyser un nombre entier. Beaucoup mieux pour juste opérer sur le flotteur directement avec Math.floor().

10
répondu jfriend00 2011-09-08 20:16:39