redis lua脚本无法安全保留浮点数精度,因redis.call("get")返回字符串,tonumber()转double会引入ieee 754误差,且redis不保证浮点解析一致性;必须用整数缩放法(如×100)全程整数运算,客户端用高精度库解析。

Redis Lua 脚本里无法安全保留浮点数精度,所有浮点运算必须在客户端做,Lua 层只负责字符串透传或整数缩放。
为什么 redis.call("get", key) 返回的 "3.14" 不能直接 tonumber() 参与计算?
因为 Redis 协议不传输浮点类型——redis.call("get", key) 返回值永远是字符串,哪怕内容是 "3.14"。Lua 的 tonumber() 会把它转成 double,但 IEEE 754 双精度浮点数对某些小数(如 0.1 + 0.2)天然不精确;更关键的是,Redis 本身不保证浮点字符串解析的一致性:比如 "1e5" 可能被 tonumber() 解析为 100000.0,再用 tostring() 回写就丢掉了原始格式。
- 不要依赖
type(x) == "number"判断是否“是浮点数”——Lua 里它只是 double,且来源不明 - 若脚本内需做加减乘除,必须提前约定缩放倍数(如 ×100 存整数),避免走
tonumber()→ 算术 →tostring()这条链 - Redis 命令如
incrbyfloat是服务端原生实现,精度由 Redis 控制,但它的返回值仍是字符串,客户端仍需自行解析
怎样让 Lua 脚本返回“看起来像浮点数”的值而不失真?
唯一可靠方式是把浮点数当字符串处理:不调用 tonumber(),不参与算术,只做拼接、截取、条件判断等字符串操作。例如存余额 "123.45",脚本里直接 redis.call("set", "balance", "123.45"),返回时也原样 return "123.45"。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 客户端收到字符串后,再用语言自带的高精度库(如 Python 的
decimal、JS 的BigNumber)解析 - 若需在脚本中做简单比较(如
"123.45" > "99.99"),可用字符串字典序 + 小数点位置校验,但仅限固定格式场景 - 避免用
string.format("%.2f", x)生成浮点字符串——x 已是 double,format 只是对近似值四舍五入,不是修复精度
想用 Lua 做浮点累加?绕开 tonumber() 的唯一可行路径
用整数缩放法:把浮点数乘以固定倍数转成整数,全程用整数运算,最后再转回字符串带小数点。例如金额单位为分,123.45 存为 12345,脚本里只做 val + ARGV[1](假设 ARGV[1] 也是整数分)。
- 客户端传参必须已是整数(如 PHP 传
12345而非123.45),否则tonumber(ARGV[1])一步就失真 - 脚本内所有中间结果保持整数,最后用
string.format("%d.%02d", math.floor(val/100), val % 100)拼出字符串(注意负数要单独处理) - Redis 7.0+ 对超大整数更严格,
val若超过2^53-1(约 9e15),即使作为整数也会在cjson.decode或某些转换中静默截断,所以缩放倍数不宜过大
真正麻烦的不是“怎么算”,而是“谁来定义精度边界”:Redis Lua 不提供任意精度支持,也不校验浮点语义。一旦你让数字进过 tonumber() 或 cjson.decode(),精度就不可逆地丢了——这个点最容易被忽略,且无法靠后续 encode 或 format 补救。










