防止 ActiveRecord 舍入小的数字属性 (< 1e-20)?
Posted
技术标签:
【中文标题】防止 ActiveRecord 舍入小的数字属性 (< 1e-20)?【英文标题】:Prevent ActiveRecord from rounding small numerical attributes ( < 1e-20)? 【发布时间】:2021-11-08 03:20:17 【问题描述】:我需要我的 Rails 应用程序处理一些非常小的数字,即小于 10e-20
。为此,我需要在三个不同的系统之间进行交互,我的数据库 (Postgres)、ActiveRecord 和 Ruby 本身。 Ruby 和 Postgres 似乎都乐于处理小到 1e-307 的数字。但是我正在努力让 ActiveRecord 发挥作用。
Postgres documentation
双精度类型的范围通常在 1E-307 到 1E+308 左右,精度至少为 15 位
Ruby documentation
最小值 双精度浮点中的最小正归一化数。通常默认为 2.2250738585072014e-308。
所以 Ruby 和 Postgres 都应该可以处理像 10e-307
这样小的数字并且大约有 15 位小数。
考虑以下记录
我的“项目”记录有一个排名属性。
# schema.rb
create_table "items", id: :uuid, default: -> "gen_random_uuid()" , force: :cascade do |t|
t.float "rank"
...
end
我可以从 PSQL 中看到一条特定记录的 this 值是1.24324e-20
:
ActiveRecord 模糊了精度
但是,当我通过 ActiveRecord 读取此值时,它会将其四舍五入为 1 个有效数字:
myItem = Item.find('a60e5947-6e75-4c4e-8b54-c13887ad6bab')
myItem.rank
# => 0.1e-19
# (this should be 0.124324e-19)
我可以使用原始 SQL 查询再次确认精确值在其中:
ActiveRecord::Base.connection.execute("select rank from items where id = 'a60e5947-6e75-4c4e-8b54-c13887ad6bab'").values
#=> [[1.24324e-20]]
这不是红宝石问题...
我想排除 Ruby 没有对数字进行四舍五入的可能性,所以我将存储在 Postgres 中的值直接打印到控制台中以检查我是否可以操作它:
1.24324e-20 + 1.1e-20
# => 2.34324e-20 (correct)
这不是 Rails 命令行格式问题
由于 Rails 用于打印到命令行的格式有时会掩盖值,因此我也想检查一下。为了确认这不仅仅是格式问题,我尝试将数字乘以 10e20 并添加另一个数字,以查看精度是否只是隐藏在 Rails 格式中的某个位置:
myItem.rank
# => 0.1e-19
i.rank * 1e20 + 1.001
# => 2.001
# (the answer should be 2.244239)
计算中忽略原始数字 (1.34324
) 的精度。所以这不是命令行格式问题。
为什么 ActiveRecord 不尊重原始精度?
我需要做些什么才能让 ActiveRecord 跟上 Postgres 和 Ruby 的精度?
请注意:我不想切换数据库列类型
8 位浮点列类型非常适合我希望存储的数字。我不需要疯狂的精确度,我只需要非常非常小的数字。我可以将数据库列切换为decimal
或numeric
,但存储的数据量完全没有必要。
Float 非常适合我的需要 - 我只需要 ActiveRecord 才能从数据库中正确读取它......
【问题讨论】:
您使用的是什么版本?我刚刚用 Ruby 2.7.4 和 Rails 6.1.4.1 尝试过这个,似乎保留了像1.24324e-20
这样的小数字的精度。
我正在使用 Ruby 2.6.6 和 Rails 6.1.4.1...
咳咳——我知道问题出在哪里了……在下面回答
【参考方案1】:
咳咳。所以事实证明,在 rank
属性的先前数据库迁移期间,我已为其分配了具有固定精度的 decimal
类型。
虽然我已将数据库迁移到使用 float
作为该属性,但我没有重新加载我的控制台,因此它正在打印出被之前的 decimal
精度规范截断的值。
令我惊讶的是,不仅控制台需要重新启动,我的开发服务器也需要重新启动,我以为它自己更新了,但显然它没有。
无论如何。谜团已揭开。呵呵。
【讨论】:
强烈建议避免使用这种精度的浮点数,而是使用小数并设置适当的精度。如果精度很重要,浮点数学是一个非常糟糕的主意 不幸的是,我真的需要花车的速度。如果我使用小数(在 javascript 中),那么与字符串之间的转换确实会减慢一切。所以我的方法是监控区分项目所需的精度,然后在低于临界水平时重新排列它们以上是关于防止 ActiveRecord 舍入小的数字属性 (< 1e-20)?的主要内容,如果未能解决你的问题,请参考以下文章