【发布时间】:2021-11-08 03:20:17
【问题描述】:
我需要我的 Rails 应用程序处理一些非常小的数字,即小于 10e-20。为此,我需要在三个不同的系统之间进行交互,我的数据库 (Postgres)、ActiveRecord 和 Ruby 本身。 Ruby 和 Postgres 似乎都乐于处理小到 1e-307 的数字。但是我正在努力让 ActiveRecord 发挥作用。
双精度类型的范围通常在 1E-307 到 1E+308 左右,精度至少为 15 位
最小值 双精度浮点中的最小正归一化数。通常默认为 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...
-
咳咳——我知道问题出在哪里了……在下面回答
标签: ruby-on-rails rails-activerecord precision