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を使用。

4

2 に答える 2

0

これは浮動小数点演算の問題です。JRuby は、Java のBigDecimalクラスを Ruby のクラスにラップしますBigDecimal。そのため、BigDecimal値には、テキスト表現と実際の値の間にわずかな違いが含まれています。==それらを比較するために使用しないでください。

これらの仕様は MRI でも同様に失敗することに注意してください。

于 2011-11-24T03:14:34.767 に答える
0

精度が落ちているわけではなく、一部の区分が で表現できないということBigDecimalです。

問題は、JRuby が 100.0/10200.0 などのときにスローすべき例外を飲み込む/定義する理由ですArithmeticException。 Java の丸めモード) (以下に追加)。

独自の丸めモードを設定するか、許容可能なデルタ比較を行ってみてください (用語を忘れてしまいました)。

例外

java.lang.ArithmeticException: Non-terminating decimal expansion;
    no exact representable decimal result.
于 2011-11-23T15:54:02.793 に答える