Javaでプリミティブdouble
の等価性を扱った経験がある人からのアドバイスをお願いします。丸めエラーが発生する可能性があるため、2つの倍数_d1 == d2
_および_d1
_に_d2
_を使用するだけでは不十分です。
私の質問は:
JavaのDouble.compare(d1,d2) == 0
は、丸めエラーをある程度処理していますか? 1.7ドキュメント で説明されているように、_0
_が_d1
_と数値的に等しい場合、値_d2
_を返します。数値的に等しいという意味が正確に何を意味するか誰か誰かいますか?
一部のデルタ値に対して相対誤差計算を使用して、推奨するデルタの一般的な(アプリケーション固有ではない)値はありますか?以下の例をご覧ください。
以下は、相対エラーを考慮して等価性をチェックするための一般的な関数です。単純な演算+、-、/、*演算からの丸め誤差の大部分をキャプチャするために、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));
}
_
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
区間演算 を使用して、累積された丸め誤差を追跡できます。ただし、実際には、エラーの間隔があまりにも悲観的になります。
CompareToのjavadocから
あなたはこれを見つけるかもしれません記事非常に役立つ
あなたが望むならあなたはのようにチェックすることができます
double epsilon = 0.0000001;
if ( d <= ( 0 - epsilon ) ) { .. }
else if ( d >= ( 0 + epsilon ) ) { .. }
else { /* d "equals" zero */ }
あなたはこのようなものを試すことができます(テストされていません):
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を追加することです(「丸め」のため)が、その後、最上位ビットを越えてずっとリップルを心配する必要があります。