You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby on Rails中使用update_all避免浮点rounding error的方案

问题

假设有一个名为somethings的表,包含类型为float的value列,表结构如下:

# == Schema Information
#
# Table name: somethings
#
# id         :bigint   not null, primary key
# value      :float    not null
# created_at :datetime not null
# updated_at :datetime not null

执行以下两次update_all操作:先将所有记录的value设为0.4,再执行value = value - 0.1,结果value列出现0.30000000000000004这类精度误差。

已知用find_each循环更新可避免该问题,但当表数据量较大时性能会严重下降,因此希望尽量用update_all实现无精度误差的更新,该如何操作?

解决方案

1. 改用精确数值类型存储(推荐)

float类型属于单精度浮点数,天生存在二进制无法精确表示部分十进制小数的问题(比如0.1)。要从根源解决误差问题,建议把value列的数据类型改成decimal(对应数据库的DECIMAL/NUMERIC类型),定义时指定精度和刻度,示例迁移文件如下:

change_column :somethings, :value, :decimal, precision: 10, scale: 2

改成decimal类型后,所有基于数据库的update_all计算都会是精确的,不会出现浮点误差。

2. 在数据库层面用精确计算临时规避(不修改字段类型)

如果暂时无法修改字段类型,可以在update_all的SQL语句里通过类型转换,用精确数值完成计算后再转回float:

  • PostgreSQL环境:
    Something.update_all("value = (value::numeric - 0.1)::float")
    
  • MySQL环境:
    Something.update_all("value = CAST(CAST(value AS DECIMAL(10,2)) - 0.1 AS FLOAT)")
    

这种方式先将float转成精确的decimal类型执行计算,再转换回float存储,能避免浮点运算的误差累积。

3. 合并连续更新操作(减少浮点运算次数)

如果两次更新是连续执行的逻辑,可以直接合并成一次update_all,跳过中间的浮点赋值步骤:

Something.update_all(value: 0.3)

这种方式从根源避免了两次浮点操作带来的误差,但仅适用于更新逻辑可以直接合并的场景。

内容的提问来源于stack exchange,提问作者makoto_atarashi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 16:32:29