Question about division behavior

Hi All,

I’m in the process of add JRuby support to a gem, and I ran into a
surprising behavior that I believe may be a bug. Specifically, the
division operation is not giving me a correct result with an integer
numerator and a denominator that is an expression that results in a
rational, non-integer value.

In a test case in the gem, a value was being calculated from two
variables:

total: 30
rate: 0.05

And the result being calculated is:

result = total / (1 + rate)

Using JRuby 1.7.4 (1.9.3p392) 2013-05-16, this result is zero
Using MRI 2.0.0-p247 (and presumably earlier MRI rubies, as this test
case
has been in place for a while), this result is 28.57142857142857

I can force JRuby to calculate the expected value (to some precision) by
forcing total to a float -

result = total.to_f / (1 + rate)

But in my view this shouldn’t be necessary, and it isn’t consistent with
MRI behavior.

I’m not entirely clear why JRuby is behaving in this fashion, and to me
it
looks like a bug. Can anyone shed any light?

Thanks.

Best,

Peter

interesting, I don’t get the same results i.e. what I get checks with
the expected MRI output

jruby 1.7.4 (1.9.3p392) 2013-05-17 fffffff on Java HotSpot™ 64-Bit
Server VM 1.7.0_21-b12 [darwin-x86_64]
monteiro:~ charles$ irb
jruby-1.7.4 :001 > 30/1.05
=> 28.57142857142857

Charles M.
[email protected]

Me too:

jruby-1.7.4 :001 > 30/1.05
=> 28.57142857142857
jruby-1.7.4 :002 > RUBY_VERSION
=> “1.9.3”

  • Keith

Can you give us more context? Maybe share the source code for your gem,
ideally with a failing test?

Doing exactly what you describe behaves fine for me:

$ ruby -v
jruby 1.7.4 (1.9.3p392) 2013-05-16 2390d3b on OpenJDK 64-Bit Server VM
1.7.0_25-b30 [linux-amd64]
$ irb
irb(main):001:0> total = 30
=> 30
irb(main):002:0> rate = 0.05
=> 0.05
irb(main):003:0> result = total / (1 + rate)
=> 28.57142857142857
irb(main):004:0> result
=> 28.57142857142857
irb(main):005:0>

On Thursday, August 22, 2013, Peter M. Goldstein wrote:

Hi All,

I'm in the process of add JRuby support to a gem, and I ran into a
surprising behavior that I believe may be a bug.  Specifically, the
division operation is not giving me a correct result with an integer
numerator and a denominator that is an expression that results in a
rational, non-integer value.

In a test case in the gem, a value was being calculated from two
variables:

total: 30
rate: 0.05

And the result being calculated is:

result = total / (1 + rate)

Using JRuby 1.7.4 (1.9.3p392) 2013-05-16, this result is zero
Using MRI 2.0.0-p247 (and presumably earlier MRI rubies, as this
test case has been in place for a while), this result
is 28.57142857142857

I can force JRuby to calculate the expected value (to some
precision) by forcing total to a float -

result = total.to_f / (1 + rate)

But in my view this shouldn't be necessary, and it isn't consistent
with MRI behavior.

I'm not entirely clear why JRuby is behaving in this fashion, and to
me it looks like a bug.  Can anyone shed any light?

Thanks.

Best,

Peter

I think the key is that total is a BigDecimal, not an integer, which I
didn’t realize until I ran some Pry inspection. See below for details.

The gem is spree/spree, master branch. I’ve got a JRuby compatible
branch
here -
https://github.com/petergoldstein/spree/tree/feature/add_jruby_to_travis
.
Note that branch already includes the forced .to_f on total.

The failing test can be found at
spree/core/spec/models/spree/calculator/default_tax_spec.rb:64

Here’s some Pry output from a binding call just before the method runs
into
trouble. As you can

From:
/Users/peter/Development/apps/petergoldstein/spree/core/app/models/spree/calculator/default_tax.rb
@ line 51 Spree::Calculator::DefaultTax#deduced_total_by_rate:

50: def deduced_total_by_rate(total, rate)

=> 51: binding.pry
52: round_to_two_places(total - ( total / (1 + rate.amount) ) )
53: end

[1] pry(#Spree::Calculator::DefaultTax)> total / (1 + rate.amount)
=> #<BigDecimal: 30.0>
[2] pry(#Spree::Calculator::DefaultTax)> total
=> #<BigDecimal: 30.0>
[3] pry(#Spree::Calculator::DefaultTax)> rate
=> #<Spree::TaxRate:0x6ab6 @name=“Spree::TaxRate_1001”>
[4] pry(#Spree::Calculator::DefaultTax)> rate.amount
=> 0.05
[5] pry(#Spree::Calculator::DefaultTax)> 1 + rate.amount
=> 1.05
[6] pry(#Spree::Calculator::DefaultTax)> 30/1.05
=> 28.57142857142857
[7] pry(#Spree::Calculator::DefaultTax)> rate.amount.class
=> Float
[8] pry(#Spree::Calculator::DefaultTax)> total.class
=> BigDecimal
[9] pry(#Spree::Calculator::DefaultTax)> BigDecimal.new(30)
=> #<BigDecimal: 30.0>
[10] pry(#Spree::Calculator::DefaultTax)> BigDecimal.new(30)/1.05
=> #<BigDecimal: 30.0>

Note that total is actually a BigDecimal, which appears to be key to the
issue.

Ah! Yes. There is an easy to reproduce bug with division and BigDecimal.

The fix, Fix for BigDecimal#/ by tychobrailleur · Pull Request #797 · jruby/jruby · GitHub, is in master and will
be released in 1.7.5.

Here is a possible workaround:
Division with BigDecimal not correct · Issue #648 · jruby/jruby · GitHub.

On Thursday, August 22, 2013, Peter M. Goldstein wrote:

I think the key is that total is a BigDecimal, not an integer, which
I didn't realize until I ran some Pry inspection.  See below for
details.

The gem is spree/spree, master branch.  I've got a JRuby compatible
branch here -
https://github.com/petergoldstein/spree/tree/feature/add_jruby_to_travis
.  Note that branch already includes the forced .to_f on total.

The failing test can be found at
spree/core/spec/models/spree/calculator/default_tax_spec.rb:64

Here's some Pry output from a binding call just before the method
runs into trouble.  As you can

From:
/Users/peter/Development/apps/petergoldstein/spree/core/app/models/spree/calculator/default_tax.rb
@ line 51 Spree::Calculator::DefaultTax#deduced_total_by_rate:

     50: def deduced_total_by_rate(total, rate)
  => 51:   binding.pry
     52:   round_to_two_places(total - ( total / (1 + rate.amount) ) 

)
53: end

[1] pry(#<Spree::Calculator::DefaultTax>)> total / (1 + rate.amount)
=> #<BigDecimal: 30.0>
[2] pry(#<Spree::Calculator::DefaultTax>)> total
=> #<BigDecimal: 30.0>
[3] pry(#<Spree::Calculator::DefaultTax>)> rate
=> #<Spree::TaxRate:0x6ab6 @name="Spree::TaxRate_1001">
[4] pry(#<Spree::Calculator::DefaultTax>)> rate.amount
=> 0.05
[5] pry(#<Spree::Calculator::DefaultTax>)> 1 + rate.amount
=> 1.05
[6] pry(#<Spree::Calculator::DefaultTax>)> 30/1.05
=> 28.57142857142857
[7] pry(#<Spree::Calculator::DefaultTax>)> rate.amount.class
=> Float
[8] pry(#<Spree::Calculator::DefaultTax>)> total.class
=> BigDecimal
[9] pry(#<Spree::Calculator::DefaultTax>)> BigDecimal.new(30)
=> #<BigDecimal: 30.0>
[10] pry(#<Spree::Calculator::DefaultTax>)> BigDecimal.new(30)/1.05
=> #<BigDecimal: 30.0>

Note that total is actually a BigDecimal, which appears to be key to
the issue.

Bruce,

Thanks for elucidating the issue. I’ll look into that workaround.

Best,

Peter