正確に合計することと効果的に比較することには違いがあります。前者が欲しいと言いますが、後者が欲しいようです。基礎となる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