BigDecimal计算出错

时间:2011-11-23 15:11:56

标签: ruby jruby bigdecimal rounding-error

考虑以下规范:

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

方法total_percent以百分比计算值列表的总差异。请忽略算法本身(通过查看第一个和最后一个值可以实现相同的结果。)

规范失败,因为计算结果不等于0.0。问题是它在哪里失去精确度。

编辑:在OS X 10.7.2上使用JRuby 1.6.5。

2 个答案:

答案 0 :(得分:0)

并不是因为它失去了精确度,而是BigDecimal无法表示某些划分。

问题是为什么JRuby吞下/定义它在100.0 / 10200.0等时应该抛出的异常.JRuby可以定义一个舍入模式,或者它的运算符可以将它们自己包裹在ArithmeticException的catch中由Java中的相同计算(没有舍入模式)生成(附后在下面)。

尝试设置自己的舍入模式,或进行可接受的delta比较(我忘记了术语)。

例外

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

答案 1 :(得分:0)

这是浮点运算的问题。 JRuby将Java的BigDecimal类包装到Ruby的BigDecimal类中。因此,BigDecimal值包含文本表示与实际值之间的微小差异。您不应该使用==来比较它们。

请注意,这些规格在MRI方面同样失败。