2011-01-04 16 views
3

私は私のRailsアプリケーションでこれを実行します。Railsの精度誤差

my_envelope.transactions.sum(:amount) 

このSQLは、ログファイルに示されている:

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

そして、この値が返されます。

<BigDecimal:1011be570,'0.2515999999 9999997E2',27(27)> 

ご覧のとおり、値は25.159999です。それは25.16でなければなりません。同じSQLをデータベース上で自分で実行すると、正しい値が返されます。

浮動小数点数には精度問題があることがわかっていますが、BigDecimalが返されています。 SQLの列型は10進数です。私はsqlite3(3.6.17)とsqlite3-ruby(1.3.2)を使っています。何か案は?私が直接SQLite3の-rubyのインターフェースを使用して、これを実行したときにここで

アップデート1

は結果があります。

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

この番号のクラスはFloatです。 btwの場合、合計値は-40.25、100、-34.59です。より多くの研究の後

アップデート2

、これはちょうどsqlite3の動作方法であることが判明しました。これはsqlite3-rubyにdouble(Ruby Floatと同じ)を返し、sqlite3-rubyはRailsにFloatとして渡します。次に、列の型が10進数なので、RailsはそれをBigDecimalに変換します。 Ruby 1.9より前では、Rubyはこの数値を丸めてしまい、問題は見えませんでした。

+0

質問を更新して、使用しているデータベースサーバーとそのアクセスに使用しているドライバーを指定してください。 – noodl

+0

これは私のテスト環境でsqlite3(3.6.17)を使っていて、sqlite3-ruby(1.3.2)を使っています。 – dontangg

+0

sqlite3-rubyが集計の値を返すためにFloatを作成していると思われます。これを避けるために結果を文字列にキャストしてみてください。私はそれが理想的な解決策ではないことを知っていますが、それは少なくとも問題を特定するのに役立ちます。 – noodl

答えて

2

これは洗練された解決策ではありませんが、集計計算の値をクエリのTEXTにキャストしてFloatオブジェクトの作成をバイパスすることができます。これにより、丸め誤差が「修正」されます。より良い解決策(sqlite3-rubyドライバをパッチするなど)が見つかった場合は、この質問を更新してください。あなたがアクティブレコードは、文字列を必要とし、その不正確なISO浮動小数点の問題にフロートをバイパスのBigDecimalのコンストラクタを呼び出すことができるようにしている文字列にキャストすることにより

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

ところで、あなたのテーブルの名前をつけることをお勧めします。transactionsこれは、ある時点で他のクラス名やデータベース固有のキーワードと競合することになります。

+0

このすべてのおかげで、ありがとう。これは今のところ良い解決策です。私はsqlite3-rubyのバグを提出しました。私は時間があれば、私はそれを自分で修正するかもしれませんが、私は生産にsqliteを使用していないので、あまりにもあまり心配するつもりはありません。 – dontangg