6

これはもちろん壊れています:

(0.1 + 0.1 + 0.1) => 0.30000000000000004

(0.1 + 0.1 + 0.1) == 0.3   # false

完全な合計は必要ありません。2 つの Float が同じ値であると言えれば十分です。私が理解できる最善の方法は、方程式の両辺を掛けて丸めることです。これが最善の方法ですか?

((0.1 + 0.1 + 0.1) * 1000).round == (0.3 * 1000).round

更新: Ruby v1.8.7 に固執しています。

4

2 に答える 2

1

正確に合計することと効果的に比較することには違いがあります。前者が欲しいと言いますが、後者が欲しいようです。基礎となるRuby浮動小数点演算はIEEEであり、累積エラーを最小限に抑えるための賢明なセマンティクスを備えていますが、すべての値を正確に表すことができない表現を使用する場合は常に存在します。エラーを正確にモデル化するために、FP加算は正確な値を生成するべきではなく、間隔を生成する必要があり、さらに加算は間隔で動作します。

実際には、多くのアプリケーションはエラーを詳細に説明する必要はありません。計算を行うだけで、比較は正確ではなく、出力される10進表現は丸められる必要があることに注意してください。

これは、比較に役立つFloatの簡単な拡張機能です。それまたはそれのようなものはstdlibにあるべきですが、そうではありません。

class Float
  def near_enough?(other, epsilon = 1e-6)
    (self - other.to_f).abs < epsilon.to_f
  end
end

pry(main)> (0.1 + 0.1 + 0.1).near_enough?(0.3)
=> true
pry(main)> (0.1 + 0.1 + 0.1).near_enough?(0.3, 1e-17)
=> false
pry(main)> ( [0.1] * (10**6) ).reduce(:+).near_enough?(10**5, 1e-5)
=> true
pry(main)> ( [0.1] * (10**6) ).reduce(:+).near_enough?(10**5)
=> false

一般的なケースでは、適切なものepsilonを選択するのは難しい場合があります。すべてのコンピューター科学者が浮動小数点演算について知っておくべきことを読む必要があります。ブルース・ドーソンの浮動小数点トリックのブログは素晴らしいと思いました。これが浮動小数点数の比較に関する彼の章です。

精度が本当に気になる場合は、正確な表現を使用して算術演算を行うことができます。RubyはRationalクラス(1.8に戻っても)を提供します。これにより、分数に対して正確な算術演算を実行できます。

pry(main)> r=Rational(1,10)
=> (1/10)
pry(main)> (r + r + r) == Rational(3,10)
=> true
pry(main)> (r + r + r) == 0.3
=> true
pry(main)> r.to_f
=> 0.1
pry(main)> (r + r + r).to_f
=> 0.3
于 2013-03-15T15:42:05.413 に答える
-1

round メソッドは、丸める小数点以下の桁数の指定をサポートしています: http://www.ruby-doc.org/core-1.9.3/Float.html#method-i-round

そう

 (0.1 + 0.1 + 0.1).round(1) == (0.3).round(1)

……いいはず。

于 2013-03-15T09:27:50.777 に答える