web-dev-qa-db-ja.com

Javaで等しいかどうかのdouble値の比較。

Javaでプリミティブdoubleの等価性を扱った経験がある人からのアドバイスをお願いします。丸めエラーが発生する可能性があるため、2つの倍数_d1 == d2_および_d1_に_d2_を使用するだけでは不十分です。

私の質問は:

  1. JavaのDouble.compare(d1,d2) == 0は、丸めエラーをある程度処理していますか? 1.7ドキュメント で説明されているように、_0_が_d1_と数値的に等しい場合、値_d2_を返します。数値的に等しいという意味が正確に何を意味するか誰か誰かいますか?

  2. 一部のデルタ値に対して相対誤差計算を使用して、推奨するデルタの一般的な(アプリケーション固有ではない)値はありますか?以下の例をご覧ください。

以下は、相対エラーを考慮して等価性をチェックするための一般的な関数です。単純な演算+、-、/、*演算からの丸め誤差の大部分をキャプチャするために、deltaのどの値を推奨しますか?

_public static boolean isEqual(double d1, double d2) {
    return d1 == d2 || isRelativelyEqual(d1,d2);
}

private static boolean isRelativelyEqual(double d1, double d2) {
    return delta > Math.abs(d1- d2) / Math.max(Math.abs(d1), Math.abs(d2));
}
_
9
chr0mzie

10のオーダーでデルタ値を試すことができます-15 しかし、一部の計算ではより大きな丸め誤差が発生することがわかります。さらに、より多くの操作を行うと、丸め誤差が累積されます。

特に悪いケースの1つは、2つのほぼ等しい数値(たとえば1.0000000001-1.0)を減算し、その結果を0.0000000001と比較する場合です。

したがって、すべての状況に適用できる一般的な方法を見つけることはほとんど期待できません。特定のアプリケーションで期待できる精度を常に計算し、この精度よりも近い場合は結果が等しいと見なす必要があります。

たとえばの出力

public class Main {

    public static double delta(double d1, double d2) {
        return Math.abs(d1- d2) / Math.max(Math.abs(d1), Math.abs(d2));
    }

    public static void main(String[] args) {
        System.out.println(delta(0.1*0.1, 0.01));
        System.out.println(delta(1.0000000001 - 1.0, 0.0000000001));
    }

}

です

1.7347234759768068E-16
8.274036411668976E-8

区間演算 を使用して、累積された丸め誤差を追跡できます。ただし、実際には、エラーの間隔があまりにも悲観的になります。

6
Henry

CompareToのjavadocから

  • このメソッドでは、Double.NaNはそれ自体と等しく、他のすべてのdouble値(Double.POSITIVE_INFINITYを含む)よりも大きいと見なされます。
  • この方法では、0.0dは-0.0dより大きいと見なされます。

あなたはこれを見つけるかもしれません記事非常に役立つ

あなたが望むならあなたはのようにチェックすることができます

double epsilon = 0.0000001;
if      ( d <= ( 0 - epsilon ) ) { .. }
else if ( d >= ( 0 + epsilon ) ) { .. }
else { /* d "equals" zero */ }
2
Rahul Tripathi

あなたはこのようなものを試すことができます(テストされていません):

public static int sortaClose(double d1, double d2, int bits) {
    long bitMask = 0xFFFFFFFFFFFFFFFFL << bits;
    long thisBits = Double.doubleToLongBits(d1) & bitMask;
    long anotherBits = Double.doubleToLongBits(d2) & bitMask;

    if (thisBits < anotherBits) return -1;
    if (thisBits > anotherBits) return 1;
    return 0;                        
}

「ビット」は通常、カットオフの精度に応じて1〜4程度になります。

洗練化は、マスキングの前にゼロ化される最初のビットの位置に1を追加することです(「丸め」のため)が、その後、最上位ビットを越えてずっとリップルを心配する必要があります。

2
Hot Licks