BigDecimal 计算错误

发布于 2024-12-18 01:56:42 字数 819 浏览 2 评论 0原文

考虑以下规范:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

┊风居住的梦幻卍 2024-12-25 01:56:42

这并不是说它失去了精度,而是有些除法无法用 BigDecimal 表示。

问题是为什么 JRuby 吞没/定义了当你 100.0/10200.0 等时它应该抛出的异常。JRuby 可能定义舍入模式,或者它的运算符可能将自己包装在 ArithmeticException 的捕获中在 Java 中通过相同的计算(没有舍入模式)生成(见下文)。

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

例外

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

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.
茶底世界 2024-12-25 01:56:42

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

请注意,这些规格对于 MRI 来说同样失败。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文