Redis中使用Lua脚本时,需要小心处理整数溢出的问题。因为Redis使用64位双精度浮点数来表示数字,而Lua中的数字类型默认是32位有符号整数。如果Lua脚本中的数字超过了32位的范围,就会导致整数溢出错误。为了避免这种问题,需要使用bit库来处理大整数。

我正在使用redis lua,需要在一个长达53位的字段上执行位逻辑运算(redis有序集成员分数的整数部分的默认长度)。

但似乎我运气不好:

127.0.0.1:6379> eval 'return bit.lshift(1, 30) ' 0
(integer) 1073741824
127.0.0.1:6379> eval 'return bit.lshift(1, 31) ' 0
(integer) -2147483648

它似乎只能在30位上执行bit。*运算,然后溢出(32位有符号整数)

我使用的是Linux 64位,redis也编译为64位。 这似乎是一个bit库的限制:

http://bitop.luajit.org/api.html

请注意,所有位操作返回有符号32位数字(原理)。 默认情况下,这些数字以有符号的十进制数表示。

另一方面...

eval 'return math.pow(2, 53) ' 0
(integer) 9007199254740992

有什么更好的办法来解决这个问题吗?

附言有人会说把这个逻辑移动到客户端——但我不能。这一块非常复杂,需要与数据紧密配合。

点赞
用户134758
用户134758

似乎 bit.* 只能处理 30 位,然后溢出(32 位有符号整数)

事实上不是这样。LuaJIT的BitOp可以处理32位有符号整数。这就是为什么2^31是负数的原因。BitOp文档解释了使用有符号int32而不是无符号的原因是因为体系结构兼容性问题:

定义结果类型为无符号数字不会安全跨越平台。因此,所有位操作都被定义为返回带符号32位数字范围内的结果

http://bitop.luajit.org/semantics.html

有时将位操作的结果与常数进行比较可能会有问题。在这种情况下,需要使用bit.tobit()规范化常量值。例如:

> = bit.lshift(1, 31) == 2147483648
false
> = bit.lshift(1, 31) == bit.tobit(2147483648)
true

无论如何,LuaJIT的BitOp模块仅限于32位整数。

另一方面,如果您需要的所有按位操作都是lshiftrshift,则可以在普通Lua中编写这些函数:

local function lshift(n, b)
   return n * 2^b
end

local function rshift(n, b)
   return n / 2^b
end
2015-11-02 20:53:50