Math.random() 返回大于一的值?

发布于 2024-12-03 15:41:09 字数 991 浏览 3 评论 0原文

在使用 JavaScript 中的随机数时,我发现了一个令人惊讶的错误,大概是在 Google Chrome 中的 V8 JavaScript 引擎中。考虑一下:

// 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())有关,但我仍然不确定为什么会发生这种情况。

While playing around with random numbers in JavaScript I discovered a surprising bug, presumably in the V8 JavaScript engine in Google Chrome. Consider:

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

Now when I run testRand5() I get the following results (of course, differing slightly with each run, you might need to set "max" to a higher value to reveal the bug):

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

Interestingly, I see comparable results in node.js, leading me to believe it's not specific to Chrome. Sometimes there are different or multiple mystery values (7, 9, etc).

Can anyone explain why I might be getting the results I see? I'm guessing it has something to do with using parseInt (instead of Math.floor()) but I'm still not sure why it could happen.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

故事和酒 2024-12-10 15:41:10

当您碰巧生成一个非常小的数字(用指数表示)时,就会出现边缘情况,例如 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

The edge case occurs when you happen to generate a very small number, expressed with an exponent, like this for example 9.546056389808655e-8.

Combined with parseInt, which interprets the argument as a string, hell breaks loose. And as suggested before me, it can be solved using Math.floor.

Try it yourself with this piece of 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
温柔女人霸气范 2024-12-10 15:41:10

当然,它是一个 parseInt()陷阱。它首先将其参数转换为字符串,这可以强制使用科学记数法,这将导致 parseInt 执行如下操作:

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

愚蠢的我...

Of course, it's a parseInt() gotcha. It converts its argument to a string first, and that can force scientific notation which will cause parseInt to do something like this:

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

Silly me...

花开浅夏 2024-12-10 15:41:10

我建议将您的随机数函数更改为:

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

这将可靠地生成 1 到 5 之间的整数值(包括 1 和 5)。

您可以在此处查看正在运行的测试函数:http://jsfiddle.net/jfriend00/FCzjF/

在这种情况下,parseInt 不是最佳选择,因为它会将浮点数转换为字符串,该字符串可以是多种不同的格式(包括科学记数法),然后尝试解析整数它。最好直接使用 Math.floor() 操作浮点数。

I would suggest changing your random number function to this:

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

This will reliably generate an integer value between 1 and 5 inclusive.

You can see your test function in action here: http://jsfiddle.net/jfriend00/FCzjF/.

In this case, parseInt isn't the best choice because it's going to convert your float to a string which can be a number of different formats (including scientific notation) and then try to parse an integer out of it. Much better to just operate on the float directly with Math.floor().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文