Ошибка точности Rails - PullRequest
       3

Ошибка точности Rails

3 голосов
/ 04 января 2011

Когда я запускаю это в моем приложении на 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 в базе данных, возвращается правильное значение.

Я немного сбит с толку, потому что я знаю, что есть проблемы точности с Float, но он возвращает 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")
=> #<SQLite3::Database:0x5242020>
irb(main):002:0> db.execute("SELECT SUM(amount) FROM transactions WHERE envelope_id = 834498537")
=> [[25.159999999999997]]

Классиз этого числа Float.Кстати, три числа это суммы -40,25, 100 и -34,59.

Обновление 2

После дополнительных исследований выясняется, что это именно тот способsqlite3 работает.Он возвращает double (так же, как Ruby Float) в sqlite3-ruby, а sqlite3-ruby просто передает его в Rails как Float.Затем Rails преобразует его в BigDecimal, потому что тип столбца является десятичным.До Ruby 1.9 Ruby округлял это число для нас, и мы не увидели бы проблемы.

1 Ответ

2 голосов
/ 05 января 2011

Это не элегантное решение, но вы можете обойти создание объекта Float, приведя значение вашего совокупного вычисления к TEXT в запросе. Это «исправляет» ошибку округления. Пожалуйста, обновите этот вопрос, если вы найдете лучшее решение (например, исправление драйвера sqlite3-ruby).

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

Приводя к строке, вы позволяете Active Record вызывать конструктор BigDecimal, который требует строку, и обходя Float с его неточными проблемами с плавающей запятой ISO.

Кстати, я сомневаюсь, что это хорошая идея назвать вашу таблицу transactions. В какой-то момент это может вступить в конфликт с каким-либо другим именем класса или ключевым словом, специфичным для базы данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...