间隔为空,Lua的math.random在处理大数时无法工作?

5

我不知道这是Lua本身的bug还是我的操作有误。在任何地方都找不到相关信息。我正在使用Lua for Windows(Lua 5.1.4):

>return math.random(0, 1000000000)
1251258

此函数将返回0到10000000000之间的随机整数,这与预期相符。对于所有其他值,此函数似乎都能正常工作。但是如果添加一个单独的0:

>return math.random(0, 10000000000)
stdin:1: bad argument #2 to 'random' (interval is empty)

任何比这个数字更大的数字都会有同样的作用。 我试图弄清楚需要多高的数字才能导致这种情况,发现了更奇怪的事情:
>return math.random(0, 2147483647)
-75617745

如果值为2147483647,则会给我负数。如果超过这个值,它会抛出错误。如果低于这个值,它就可以正常工作。

这在二进制中是0b1111111111111111111111111111111,恰好是31个二进制数字。我不确定这意味着什么。

2个回答

9

这种意外行为(bug?)是由Lua 5.1中math.random处理输入参数的方式引起的。从lmathlib.c来看:

case 2: {  /* lower and upper limits */
  int l = luaL_checkint(L, 1);
  int u = luaL_checkint(L, 2);
  luaL_argcheck(L, l<=u, 2, "interval is empty");
  lua_pushnumber(L, floor(r*(u-l+1))+l);  /* int between `l' and `u' */
  break;
}

正如你所知,在 C 语言中,标准的 int 可以表示从 -2,147,483,6482,147,483,647 的值。在你的使用情况下,将 2,147,483,647 加上 +1,会导致溢出并使值 环绕,得到的结果是 -2,147,483,648。最终结果为负数,因为你正在用一个正数与一个负数相乘。
此外,任何大于 2,147,483,647 的值都会由于溢出而失败 luaL_argcheck
有几种方法可以解决这个问题:
  • 升级到 Lua 5.2。该版本已通过将输入参数视为 lua_Number 来修复了此问题。
  • 切换到没有这个整数溢出问题的 LuaJIT。
  • 使用修复程序打补丁,并重新编译 Lua 5.1 源代码。
  • 修改您的随机范围,使其不会溢出。

4

如果你需要的范围大于随机函数所支持的范围(32位有符号整数或2 ^ 31,因为math.random在C级别),但小于Lua“number”类型的范围(基于What is the maximum value of a number in Lua?,2 ^ 52,甚至可能是2 ^ 53),那么可以尝试生成两个随机数:将第一个缩放到所需范围; 添加第二个以“填补差距”。例如,假设您想要0到2 ^ 36的范围。从math.random中最大的为2 ^ 31。那么可以这样做:

-- 2^36 = 2^31 * 2^5 so
scale = 2^5
baseRand = scale * math.random(0, 2^31)
-- baseRand is now between 0 and 2^36 but there are gaps of 2^5 in the set 
-- of possible values; fill the gaps with second random number: 
fillGap = math.random(0, 2^5)
randNum = baseRand + fillGap

这将起作用,只要所需范围小于Lua解释器的最大Lua数字,这是一个可配置的编译时参数,但如果您使用的是标准版本,则为2 ^ 52,这是一个非常大的数字(虽然不如最大的长整数2 ^ 63那么大)。
请注意,最大的正N位整数是2 ^ N-1(而不是2 ^ N),但上述技术可以应用于任何范围,例如,您可以设置scale = 10 ^ 6,然后randNum = 10 ^ 6 * math.random(0, 10 ^ 8)+ math.random(0, 10 ^ 6)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接