Math.random () возвращает значение больше единицы? - PullRequest
40 голосов
/ 08 сентября 2011

Играя со случайными числами в JavaScript, я обнаружил удивительную ошибку, предположительно в движке JavaScript V8 в Google Chrome. Рассмотрим:

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

Теперь, когда я запускаю testRand5(), я получаю следующие результаты (конечно, немного отличающиеся с каждым прогоном, вам может потребоваться установить максимальное значение "max", чтобы выявить ошибку):

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

Интересно, что я вижу сопоставимые результаты в node.js, что наводит меня на мысль, что это не относится к Chrome. Иногда существуют разные или множественные значения тайны (7, 9 и т. Д.).

Может кто-нибудь объяснить, почему я получаю результаты, которые вижу? Я предполагаю, что это как-то связано с использованием parseInt (вместо Math.floor()), но я все еще не уверен, почему это могло произойти.

Ответы [ 3 ]

76 голосов
/ 08 сентября 2011

Крайний случай возникает, когда вы генерируете очень маленькое число, выраженное показателем степени, например, это 9.546056389808655e-8.

В сочетании с parseInt, который интерпретирует аргумент как строку , ад освобождается. И, как предлагалось до меня, это можно решить с помощью Math.floor.

Попробуйте сами с этим фрагментом кода:

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
38 голосов
/ 08 сентября 2011

Конечно, это parseInt(). Сначала он преобразует свой аргумент в строку , и это может вызвать научную запись, которая заставит parseInt сделать что-то вроде этого:

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

Глупый я ...

10 голосов
/ 08 сентября 2011

Я бы предложил изменить функцию случайных чисел на:

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

Это надежно сгенерирует целочисленное значение от 1 до 5 включительно.

Вы можете увидеть свою тестовую функцию в действии здесь: http://jsfiddle.net/jfriend00/FCzjF/.

В этом случае, parseInt не лучший выбор, потому что он собирается преобразовать ваш float в строку, которая может иметь несколько различных форматов (включая научную запись), а затем попытаться проанализировать целое число из него. Намного лучше просто работать на поплавке напрямую с Math.floor().

...