RubyMoney / monetize

A library for converting various objects into `Money` objects.
MIT License
430 stars 107 forks source link

Ruby 3 Rounding Issue #156

Closed caseyprovost closed 1 year ago

caseyprovost commented 2 years ago

Floats are now translated in interesting ways in Ruby 3. Take the following example:

# rails console or IRB

9.86.to_money => 
{
           :cents => 985.9999999999999,
    :currency_iso => "USD"
}

9.86.to_money.cents #=> 985.9999999999999

9.86.to_d(Float::DIG).to_money.cents #=> 986.0

What makes this more interesting is that not all numbers seem to be treated evenly.

# rails console or IRB

3.86.to_money.cents => #> 386.0

I am not sure if this is a bug in Float in ruby proper but I have found we change how we convert floats to money by transitioning to a BigDecimal using Float::DIG...all our rounding issues went away on Ruby 3.

Is this something we should have a PR for here? Or should I be submitting a bug to Ruby?

ghost commented 2 years ago

Floats are now translated in interesting ways in Ruby 3. Take the following example:

# rails console or IRB

9.86.to_money => 
{
           :cents => 985.9999999999999,
    :currency_iso => "USD"
}

9.86.to_money.cents #=> 985.9999999999999

9.86.to_d(Float::DIG).to_money.cents #=> 986.0

What makes this more interesting is that not all numbers seem to be treated evenly.

# rails console or IRB

3.86.to_money.cents => #> 386.0

I am not sure if this is a bug in Float in ruby proper but I have found we change how we convert floats to money by transitioning to a BigDecimal using Float::DIG...all our rounding issues went away on Ruby 3.

Is this something we should have a PR for here? Or should I be submitting a bug to Ruby?

lenlo commented 2 years ago

I can't reproduce this with Ruby 3.1:

irb(main):001:0> 9.86.to_money
=> #<Money fractional:986 currency:USD>
irb(main):002:0> 9.86.to_money.cents
=> 986
irb(main):003:0> 9.86.to_d(Float::DIG).to_money.cents
=> 986
irb(main):004:0> RUBY_VERSION
=> "3.1.0"
irb(main):005:0> Gem.loaded_specs['monetize'].version
=> Gem::Version.new("1.12.0")
irb(main):006:0> Gem.loaded_specs['money'].version
=> Gem::Version.new("6.16.0")

Has this already been fixed or am I missing something?

nitsujri commented 2 years ago

I'm on Ruby 3.0.1 and don't see this issue.

irb(main):001:0> 9.86.class
=> Float
irb(main):002:0> 9.86.to_money.cents
=> 986
irb(main):003:0> 9.86.to_money
=> #<Money fractional:986 currency:USD>
irb(main):004:0> 9.86.to_money.cents
=> 986
irb(main):005:0> 9.86.to_d(Float::DIG).to_money.cents
=> 986
irb(main):007:0> RUBY_VERSION
=> "3.0.1"
irb(main):008:0> Gem.loaded_specs['monetize'].version
=> Gem::Version.new("1.12.0")
irb(main):009:0> Gem.loaded_specs['money'].version
=> Gem::Version.new("6.16.0")