Math.random() возвращает значение больше одного?
Во время игры со случайными номерами в 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()
), но я все еще не уверен, почему это может произойти.
Ответы
Ответ 1
Случай с краем возникает, когда вам приходится генерировать очень небольшое число, выраженное с помощью показателя степени, например, 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
Ответ 2
Конечно, это parseInt()
. Он сначала преобразует свой аргумент в строку, и это может заставить научную нотацию, которая заставит parseInt делать что-то вроде этого:
var x = 0.000000004;
(x).toString(); // => "4e-9"
parseInt(x); // => 4
Глупо мне...
Ответ 3
Я бы предложил изменить вашу функцию случайных чисел:
var rand5 = function() {
return(Math.floor(Math.random() * 5) + 1);
};
Это будет надежно генерировать целочисленное значение между 1 и 5 включительно.
Здесь вы можете увидеть свою тестовую функцию: http://jsfiddle.net/jfriend00/FCzjF/.
В этом случае parseInt
не лучший выбор, потому что он преобразует ваш float в строку, которая может быть несколькими различными форматами (включая научную нотацию), а затем попытаться разобрать целое число из нее. Гораздо лучше просто работать с float напрямую с помощью Math.floor()
.