【问题标题】:Avoiding Float truncation in Rails 4在 Rails 4 中避免浮点截断
【发布时间】:2015-04-08 14:24:37
【问题描述】:

我有一个带有Float 属性和对应表的 Active Record 模型:

# app/models/test.rb
class Test < ActiveRecord::Base
end

# db/schema.rb
ActiveRecord::Schema.define(version:20150208185300)

  enable_extension "plpgsql"

  create_table "tests", force: true do |t|
    t.datetime "created_at"
    t.datetime "updated_at"
    t.float    "size"
  end

end

当我坚持测试时,一切看起来都很好:

Test.create(size: 271893999999.99997)
#=> #<Test id: 1, created_at: "2015-02-07 19:43:22", updated_at: "2015-02-07 19:43:22", size: 271893999999.99997>

但是,在检索测试的大小时,它会以截断的形式返回:

Test.last.size
#=> 271894000000.0

尽管我创建它时输出误导性,但在持久化时也被截断:

Test.last
#=> #<Test id: 1, created_at: "2015-02-07 19:43:22", updated_at: "2015-02-07 19:43:22", size: 271894000000.0>

这个问题让我很困惑,因为我可以在 Ruby 中执行以下操作:

a = 123456789.123456789
#=> 123456789.123456789
a.class
#=> Float
a
#=> 123456789.123456789

在 Rails/Postgres 中使用 Float 有什么限制?如果我想要 5 个小数点的精度,我应该使用 Decimal 吗?

我正在使用 Rails 4.1.1 和 Postgres 9.3.5

【问题讨论】:

    标签: ruby-on-rails ruby postgresql rails-activerecord


    【解决方案1】:

    在 Rails/Postgres 中使用 Float 有什么限制?

    PostgreSQL 支持使用标识符 floatrealdouble 的浮点数。标识符 floatreal 是同义词。在大多数计算机上,floatreal 将为您提供至少 6 位小数精度。这就是六位小数精度的样子。

    Test.last.size
    #=> 271894000000.0
        ^^^^^^
    

    在大多数计算机上,double 将为您提供至少 15 位的十进制精度。 Ruby's float 相当于 PostgreSQL 的 double,而不是 PostgreSQL 的 real

    如果我想要 5 个小数点的精度,我应该使用 Decimal 代替吗?

    我想要小数点后 5 位的准确性 [原文如此] 这句话不能很好地转换为浮点数据类型。当你写这样的东西时

    Test.create(size: 271893999999.99997)
    

    您不是在向 PostgreSQL 询问小数点右侧的五位数字。您要求 17 位小数精度。浮点数据类型不太可能给您那么高的精度。

    您需要使用任意精度的数据类型,而不是浮点数。在数据库方面,它看起来像

    numeric(17, 5)
    decimal(17, 5)
    

    这两个声明都为您提供 17 位精度,小数点右侧最多 5 位。

    在 Rails 迁移中,您可能会这样做。

    add_column :your_table, :your_column, :decimal, :precision => 17, :scale => 5
    


    你在这里看到的
    Test.last.size
    #=> 271894000000.0
    

    严格来说,不是截断或舍入。这是“近似误差”。值271893999999.99997 不在数据库域float 中,因此PostgreSQL 使用该域中 的最接近的值。该值恰好是271894000000.0


    PostgreSQL numeric data types

    【讨论】:

      【解决方案2】:

      使用Decimal,并在你的数据库中指定precisionscale,这样你一定会有准确的计算。

      Take a look at short example

      【讨论】:

      • 我仍然不明白为什么我不能使用 Float 来完成这项任务——他们能够在 Rails 之外处理那么多小数。这是 Postgres 的限制吗?
      猜你喜欢
      • 2012-12-13
      • 2012-11-26
      • 2011-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-31
      • 1970-01-01
      相关资源
      最近更新 更多