避免Rails中的浮点截断4

时间:2015-02-08 21:46:54

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

我有一个Active Record模型,它采用Float属性和相应的表格:

# 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

2 个答案:

答案 0 :(得分:2)

  

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

PostgreSQL使用标识符 float real double 支持浮点数。标识符 float real 是同义词。在大多数计算机上, float real 将为您提供至少6位小数精度。这是十进制精度的六位数。

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

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

  

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

短语我想要5个小数点的准确度 [sic]并不能很好地转换为浮点数据类型。当你写这样的东西时

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
严格来说,

不是截断或舍入。它是近似的错误&#34;。值271893999999.99997不在数据库域float中,因此PostgreSQL使用该域中 的最接近值。该值恰好是271894000000.0

<小时/> PostgreSQL numeric data types

答案 1 :(得分:1)

使用Decimal并在数据库中指定 precision scale ,这样您就可以确保计算准确。

Take a look at short example