Конвертировать центы в долларовую строку в Ruby без использования BigDecimal - PullRequest
6 голосов
/ 18 сентября 2011

Я хочу правильно конвертировать из центов в доллары в рубине. Мне никогда не придется работать с долями центов.

Можно ли сделать это правильно (без ошибок с плавающей запятой) без необходимости использовать BigDecimal?

Например, центы в долларах

"99" => "0.99"
"324" => "3.24"

Кажется, что работает следующее, но правильно ли это?

(cents.to_i/100.0).to_s

Ответы [ 4 ]

11 голосов
/ 18 сентября 2011

Как Мишель Коль уже ответил : Взгляните на денежный камень.

Пример:

require 'money'
Money.use_i18n = false  #https://stackoverflow.com/q/31133229/676874
puts Money.new( 99, 'USD')
puts Money.new(324, 'USD')

Кажется, что следующееработать, но правильно ли это?

(cents.to_i/100.0).to_s

На первый взгляд все нормально, но:

cents = '10'
p (cents.to_i/100.0).to_s # -> '0.1'

У вас нет 2 цифр.

Альтернатива:

p '%.2f' % (cents.to_i/100.0) # -> '0.10'
5 голосов
/ 18 сентября 2011

Если они уже являются жалами, вы можете использовать манипуляции со строками и полностью обойти числовые проблемы:

# There are, of course, all sorts of ways to do this.
def add_decimal(s)
  pfx = [ '0.00', '0.0', '0.' ]
  if(pfx[s.length])
    s = pfx[s.length] + s
  else
    s = s.dup
    s[-2, 0] = '.'
  end
  s
end

add_decimal('')      #   "0.00" 
add_decimal('1')     #   "0.01" 
add_decimal('12')    #   "0.12" 
add_decimal('123')   #   "1.23" 
add_decimal('1234')  #  "12.34" 
add_decimal('12345') # "123.45"

Никаких проблем с точностью, ни плавающей запятой, ни bignums, ни Rational, ни хитрости, ни хитрости. Для работы с отрицательными значениями потребуются некоторые простые модификации, но это будет так же просто, как то, что уже есть.

4 голосов
/ 18 сентября 2011

Лично я бы не стал заново изобретать это колесо и использовать денежный камень .Из документов (выделение выделено):

Функции

Предоставляет класс Money, который инкапсулирует всю информацию об определенной сумме денег, такую ​​как ее стоимость и валюта.

Предоставляет класс Money :: Currency, который инкапсулирует всю информацию о денежной единице.

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

Представляет валюту как экземпляры Money :: Currency, обеспечивая высокий уровень гибкости.

Предоставляет API для обмена денег из одной валюты в другую.

Имеет возможность разбирать денежные и валютные строки в соответствующий объект Money / Currency.

2 голосов
/ 18 сентября 2011

Вы также можете рассмотреть возможность использования Rational. Тем не менее, я не уверен, что они преобразуются в float, когда sprintf-ed:

"%.2f" % Rational("324".to_i,100)
#=> "3.24"
"%.2f" % Rational("99".to_i,100)
#=> "0.99"
"%.2f" % Rational("80".to_i,100)
#=> "0.80"
"%.2f" % Rational("12380".to_i,100)
#=> "123.80"
...