Rails精度错误

当我在我的Rails应用程序中运行它时:

my_envelope.transactions.sum(:amount) 

此SQL显示在日志文件中:

 SQL (0.3ms) SELECT SUM("transactions"."amount") AS sum_id FROM "transactions" WHERE (envelope_id = 834498537) 

并返回此值:

  

如您所见,该值为25.159999。 它应该是25.16。 当我自己在数据库上运行相同的SQL时,返回正确的值。

我有点困惑,因为我知道浮点数有精确问题,但它返回一个BigDecimal。 SQL列类型是十进制。 我正在使用sqlite3(3.6.17)和sqlite3-ruby(1.3.2)。 有任何想法吗?

更新1

以下是使用SQLite3-ruby接口直接运行时的结果。

 $ rails c test Loading test environment (Rails 3.0.3) irb(main):001:0> db = SQLite3::Database.new("db/test.sqlite3") => # irb(main):002:0> db.execute("SELECT SUM(amount) FROM transactions WHERE envelope_id = 834498537") => [[25.159999999999997]] 

该数字的类是Float。 顺便说一句,它总和的三个数字是-40.25,100和-34.59。

更新2

经过更多的研究,结果certificate这就是sqlite3的工作方式。 它返回一个double(与Ruby Float相同)到sqlite3-ruby,而sqlite3-ruby只是将它作为Float传递给Rails。 然后,Rails将其转换为BigDecimal,因为列类型是十进制的。 在Ruby 1.9之前,Ruby会为我们舍入这个数字,我们不会看到问题。

这不是一个优雅的解决方案,但您可以通过将聚合计算的值转换为查询中的TEXT来绕过Float对象的创建。 这“修复”舍入错误。 如果您找到更好的解决方案(例如通过修补sqlite3-ruby驱动程序),请更新此问题。

 SELECT CAST(SUM(amount) AS TEXT) FROM transactions WHERE envelope_id = 834498537 

通过强制转换为字符串,您可以允许Active Record调用BigDecimal的构造函数,该构造函数需要一个字符串并绕过Float,其不准确的ISO浮点问题。

顺便说一句,我怀疑为您的表transactions命名是个好主意。 在某些时候,这必然会与某些其他类名或特定于数据库的关键字冲突。