Forum: JRuby Question about division behavior

06eac631460b22ff6c635e770e206a58?d=identicon&s=25 Peter M. Goldstein (Guest)
on 2013-08-22 20:12
(Received via mailing list)
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
2d1b64aaf61046ad166db3009fa8c7dc?d=identicon&s=25 Charles Monteiro (Guest)
on 2013-08-22 20:38
(Received via mailing list)
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(TM) 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 Monteiro
jruby@smallruby.com
486ca04f06d968004643ce5b47376ded?d=identicon&s=25 Keith B. (keith_b)
on 2013-08-22 20:49
(Received via mailing list)
Me too:

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

- Keith
147ef000d3a4fb7bd34d2fef34d55fef?d=identicon&s=25 Bruce Adams (Guest)
on 2013-08-22 20:59
(Received via mailing list)
Attachment: spacer.gif (43 Bytes)
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
06eac631460b22ff6c635e770e206a58?d=identicon&s=25 Peter M. Goldstein (Guest)
on 2013-08-22 21:14
(Received via mailing list)
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/featu...
.
 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.
147ef000d3a4fb7bd34d2fef34d55fef?d=identicon&s=25 Bruce Adams (Guest)
on 2013-08-23 00:29
(Received via mailing list)
Attachment: spacer.gif (43 Bytes)
Ah! Yes. There is an easy to reproduce bug with division and BigDecimal.

The fix, https://github.com/jruby/jruby/pull/797, is in master and will
be released in 1.7.5.

Here is a possible workaround:
https://github.com/jruby/jruby/issues/648#issuecom....

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/featu...
    .  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.
06eac631460b22ff6c635e770e206a58?d=identicon&s=25 Peter M. Goldstein (Guest)
on 2013-08-23 00:52
(Received via mailing list)
Bruce,

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

Best,

Peter
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.