2017-04-07 6 views
-1

2つのダブルスがほぼ等しいかどうかを判断するためにJavaコードを見つけようとしています。私はグーグルで多くのことをして、私がここにまとめたものを見つけました。私がエスケープし始めるところは「相対的イプシロン」の使用です。このアプローチは、私が探しているもののようです。私はイプシロンを直接指定する必要はないが、2つの引数の大きさに基づいてイプシロンを使用したいと思う。ここに私がまとめたコードがあります。私はそれについての健全性チェックが必要です。 (P.S.私は危険なことができるだけの数学を知っている。)2つのダブルスがほぼ等しいかどうかを調べる方法

public class MathUtils 
{ 
    // http://stackoverflow.com/questions/3728246/what-should-be-the- 
    // epsilon-value-when-performing-double-value-equal-comparison 
    // ULP = Unit in Last Place 
    public static double relativeEpsilon(double a, double b) 
    { 
     return Math.max(Math.ulp(a), Math.ulp(b)); 
    } 

    public static boolean nearlyEqual(double a, double b) 
    { 
     return nearlyEqual(a, b, relativeEpsilon(a, b)); 
    } 

    // http://floating-point-gui.de/errors/comparison/ 
    public static boolean nearlyEqual(double a, double b, double epsilon) 
    { 
     final double absA = Math.abs(a); 
     final double absB = Math.abs(b); 
     final double diff = Math.abs(a - b); 

     if(a == b) 
     { 
      // shortcut, handles infinities 
      return true; 
     } 
     else if(a == 0 || b == 0 || diff < Double.MIN_NORMAL) 
     { 
      // a or b is zero or both are extremely close to it 
      // relative error is less meaningful here 
      // NOT SURE HOW RELATIVE EPSILON WORKS IN THIS CASE 
      return diff < (epsilon * Double.MIN_NORMAL); 
     } 
     else 
     { 
      // use relative error 
      return diff/Math.min((absA + absB), Double.MAX_VALUE) < epsilon; 
     } 
    } 
} 
+1

1つのulpがおそらく小さ過ぎるεです。 –

+0

どのように私はそれをバンプする必要がありますに関するお勧め? – careysb

+0

ああ、@ルイスワッサーマンは、クラウスが言ったあなたのもののようです。 – careysb

答えて

1

私はこのためのライブラリを使用することになり、私が通常使用するものは、グーグルグアバライブラリあちこちDoubleMathです。 https://google.github.io/guava/releases/19.0/api/docs/com/google/common/math/DoubleMath.html

if (DoubleMath.fuzzyEquals(a, b, epsilon)) { // a and b are equal within the tolerance given } また、ファジィコンパイルがあります。

+0

ライブラリのfuzzyEqals()には、ulpを使って回避しようとしている許容値パラメータが必要です(これは明らかに正しく行っていません)。 – careysb

+3

@careysbライブラリが許容値を要求する理由は、正しい許容値がulpに依存するだけではないためです。これまでの計算を考慮して、予想される丸め誤差の範囲に依存します。 –

+0

正確には、浮動小数点/倍精度のいずれかが計算を使用して作成された場合、精度が失われる可能性があるため、許容誤差が必要になります。たとえば '(2f/11 * 9)* 11;は18.000002で、' 3f * 6'は18.0であるのに対し、比較の際には許容値が必要です。 –

0

a,b 2つのフローティング値を比較するための通常の方法である:Math.abs()は絶対値である

if (Math.abs(a-b) <= epsilon) do_stuff_if_equal; 
else      do_stuff_if_different; 

。 JAVAでコーディングしないので、そうでない場合はdoubleバリエーションを使用する必要があります。 epsilonはあなたの違いです。前述のようにulpはこれには小さすぎます。あなたは、比較している値に合った値を使用する必要があります。だからepsilonを計算するには?

これはちょっとした問題ですが、a,bの大きさを使用することは可能ですが、指数があまりにも多すぎると偽陽性を簡単に得ることができるため、堅牢ではありません。代わりに、意味のある値を使用する必要があります。たとえば、位置座標を比較している場合、イプシロンは最小詳細の分数または同じ点として考える最小距離でなければなりません。角度については、1e-6 degのようにいくらかの最小角度がありますが、値は使用する範囲と精度によって異なります。正規化<-1,1>の範囲では、通常、1e-10または1e-30を使用します。

イプシロンは、ターゲットの正確さと大きさに大きく左右され、ケースごとに非常に異なるので、均一な道を作ることで(後でepsilonを取り除く)安全ではなく、後で頭が痛むに。

これを簡単にするために、私は通常、変更可能な定数または変数(計算クラスの場合)を定義します。_zeroほとんどの場合、十分に良い値に設定してください。ある時点で問題が発生した場合は、簡単に変更することができます。

とにかく(上記のテキストは無視して)これを行うことができます:

if (Math.abs(a)>=Math.abs(b)) epsilon=1e-30*Math.abs(b); 
else       epsilon=1e-30*Math.abs(a); 

しかし、これは間違った結果につながる可能性があります。 ulpを使用し続ける場合は、Maxの代わりにMinを使用します。

関連する問題