Десятичные значения усекаются с to_f

avatar
Developer
9 августа 2021 в 06:22
243
2
2

У меня есть модель с именем Item, в которой я обновляю значение unit_price (тип данных — десятичный), в настоящее время я не устанавливаю никаких ограничений при сохранении значения, сохраняя значение как есть. Но теперь я вижу эту ошибку PG PG::NumericValueOutOfRange, когда значение превышает предел.

Итак, я просто пытался ограничить значение и что-то проверять в консоли. Ниже приведены данные. (Здесь в данных я не ставлю все десятичные значения)

#<Item id: 167199, description: "192830139", category_id: 10327, unit_id: 5596, weight: 0.1e5, unit_price: 0.4083333333659917816764132553606237816656920077972709552126705653021442494641325536062378168e1

i = Item.find 167199

i.unit_price.to_f
=> 4.083333333659918

#<Item id: 167199, description: "192830139", category_id: 10327, unit_id: 5596, weight: 0.1e5, unit_price: 0.6511366980197836882065909262763993442019943880913510722934069011050182329156169820243980265070876781866034494363303661586489199452739290976143216266200531728395970406461889852558384421962422689303402903e-2

i.unit_price.to_f
=> 0.006511366980197837

Могу ли я узнать, по какой причине to_f автоматически уменьшает предел десятичной дроби? Каков будет лучший способ решить эту проблему, я просто думал о некотором усечении с некоторым ограничением.

Источник
Stefan
9 августа 2021 в 07:37
1

Ну, поплавки имеют ограниченную точность. Вместо этого вы можете использовать BigDecimal. Другой распространенный способ решить эту проблему — хранить денежные значения в виде целых чисел, используя наименьшую возможную единицу измерения, например. 1,95 доллара США как 195 (центов).

Stefan
9 августа 2021 в 07:44
0

Кстати, а зачем вам вообще такой unit_price? Используете ли вы поплавки вне вашей базы данных? (вы не должны)

Developer
9 августа 2021 в 08:56
0

Нет, сейчас я не использую to_f на уровне ruby, в настоящее время сохраняю все, что мы получаем в результате вычислений.

Max
15 октября 2021 в 06:41
1

Зачем вам около 100 знаков после запятой?

spickermann
15 октября 2021 в 10:51
0

Существует множество статей о том, почему никогда не следует использовать числа с плавающей запятой для денежных единиц или вычислений. Этот вопрос звучит как xy проблема для меня.

Developer
16 октября 2021 в 15:12
0

Привет @spickermann, это очень большое старое приложение, так что теперь изменение с наименьшим десятичным числом будет иметь огромное влияние, да, это правильное решение. Итак, теперь я ищу решение, такое как усечение, просто чтобы избежать этой проблемы. так это усечение будет делать какое-либо округление?

Ответы (2)

avatar
Matheus Moreira
15 октября 2021 в 06:33
3

Могу ли я узнать, по какой причине to_f автоматически уменьшает предел десятичной дроби?

Причина в том, что методы to_f используются для преобразования объектов в Floats<96552012222301>, которые являются стандартными 64-битными числами с плавающей запятой двойной точности<9655203>222301. Точность этих чисел ограничена, поэтому точность исходного объекта должна автоматически уменьшаться в процессе преобразования, чтобы он соответствовал Float. Вся дополнительная точность теряется.

Похоже, вы используете класс BigDecimal. Метод BigDecimal#to_f преобразует десятичный объект произвольной точности с плавающей запятой в Float. Естественно, информация будет потеряна во время этого преобразования, если большое десятичное число будет более точным, чем позволяет Floats. Это преобразование может фактически переполниться или опуститься, если пределы превышены.

Я как раз думал об усечении с некоторым ограничением

Существует метод truncate, если вы хотите явно контролировать точность результата. Никакого округления не произойдет, для этого есть отдельный метод.

  • BigDecimal#truncate

    Удаляет всю дробную часть числа, оставляя только целое число.

    BigDecimal('3.14159').truncate #=> 3
    
  • BigDecimal#truncate(n)

    Сохраняет n цифры точности, удаляет остальные.

    BigDecimal('3.14159').truncate(3) #=> 3.141
    
Developer
16 октября 2021 в 15:15
0

Привет @Matheus, спасибо за ваше предложение, так что какую бы точность мы ни давали усечению, это просто даст правильные значения? это не будет округлять правильно?

Matheus Moreira
18 октября 2021 в 05:45
0

@Разработчик прав. Усечение просто удаляет все цифры после указанной точности. Никакого округления не произойдет. Для этого есть отдельный метод.

avatar
The Parallax
9 августа 2021 в 06:33
-1

вы можете использовать встроенный в ruby ​​метод .truncate()

например:

floatNum = 1.222222222222222
truncatedNum = floatNum.truncate(3) #3 is the number of decimal places you want
puts floatNum #returns 1.222

enter image description here

другой способ — использовать метод .round()

например:

enter image description here

Developer
9 августа 2021 в 06:52
0

Верно, это правильно, но по какой причине этот to_f автоматически уменьшает предел десятичной дроби?

The Parallax
9 августа 2021 в 06:56
0

если вы заметили, тип данных — bigdecimal. он используется для хранения больших десятичных знаков, чем float. Это работает так же, как float и double в java. Вот что я думаю. Это может быть неправильно, т.