考虑以下规范:
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。
答案 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方面同样失败。