Domanda

Consider the following spec:

require 'bigdecimal'

def total_percent(amounts)
  percent_changes = amounts.each_cons(2).map { |a|
    (a[1] - a[0]) / a[0] * BigDecimal.new('100.0')
  }
  (percent_changes.map { |pc| BigDecimal.new('1') + pc / BigDecimal.new('100') }.inject(BigDecimal.new('1'), :*) - BigDecimal.new('1')) * BigDecimal.new('100')
end

describe 'total_percent' do

  specify {
    values = [10000.0, 10100.0, 10200.0, 10000.0].map { |v|
      BigDecimal.new(v.to_s)
    }
    total_percent(values).class.should == BigDecimal
    total_percent(values).should == BigDecimal.new('0.0')
  }

end

The method total_percent calculates the total difference of a list of values in percent. Please ignore the algorithm itself (the same result can be achieved by looking at the first and last values only).

The spec fails because the result of the calculation is not equal to 0.0. The question is where is it losing precision.

Edit: Using JRuby 1.6.5 on OS X 10.7.2.

È stato utile?

Soluzione

It's not that it's losing precision, it's that some of the divisions aren't representable by a BigDecimal.

The question is why does JRuby swallow/define-away the exception it should be throwing when you 100.0/10200.0 etc. JRuby may define a rounding mode, or its operators may wrap themselves in a catch of the ArithmeticException generated by the same calculation (without a rounding mode) in Java (appended below).

Try setting your own rounding mode, or do an acceptable-delta comparison (I forget the term).

The exception

java.lang.ArithmeticException: Non-terminating decimal expansion;
    no exact representable decimal result.

Altri suggerimenti

This is an issue with floating point arithmetic. JRuby wraps Java's BigDecimal class into Ruby's BigDecimal class. As such, BigDecimal values contain small differences between the textual representation and the actual value. You should not use == to compare them.

Note that these specs fail similarly for MRI.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top