BigDecimal 计算错误
考虑以下规范:
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。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这并不是说它失去了精度,而是有些除法无法用
BigDecimal
表示。问题是为什么 JRuby 吞没/定义了当你 100.0/10200.0 等时它应该抛出的异常。JRuby 可能定义舍入模式,或者它的运算符可能将自己包装在 ArithmeticException 的捕获中在 Java 中通过相同的计算(没有舍入模式)生成(见下文)。
尝试设置您自己的舍入模式,或者进行可接受的增量比较(我忘记了这个术语)。
例外
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
这是浮点运算的问题。 JRuby 将Java 的
BigDecimal
类包装到Ruby 的BigDecimal
类中。因此,BigDecimal
值在文本表示和实际值之间包含微小差异。您不应该使用==
来比较它们。请注意,这些规格对于 MRI 来说同样失败。
This is an issue with floating point arithmetic. JRuby wraps Java's
BigDecimal
class into Ruby'sBigDecimal
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.