比较Java中的相等的双重值。

时间:2014-08-06 12:26:16

标签: java double double-precision

我想从那些在Java中使用原始double相等经验的人那里得到一些建议。由于可能的舍入错误,使用d1 == d2两个d1d2是不够的。

我的问题是:

  1. Java的Double.compare(d1,d2) == 0是否在某种程度上处理舍入错误?如1.7 documentation中所述,如果0在数字上等于d1,则返回值d2。是否有人确定它们在数字上是什么意思?

  2. 对某些增量值使用相对误差计算,是否会推荐使用delta的通用(非应用程序特定)值?请参阅下面的示例。

  3. 下面是考虑相对误差来检查相等性的通用函数。您建议使用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));
    }
    

3 个答案:

答案 0 :(得分:5)

你可以用10 -15 的顺序试验delta值,但是你会注意到一些计算会产生更大的舍入误差。此外,您所做的操作越多,将是累积的舍入误差。

一个特别糟糕的情况是,如果你减去两个几乎相等的数字,例如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

Interval arithmetic可用于跟踪累积的舍入错误。然而在实践中,错误间隔过于悲观,因为有时舍入错误也会相互抵消。

答案 1 :(得分:2)

来自compareTo的javadoc

  • 此方法认为Double.NaN等于其自身且大于所有其他double值(包括Double.POSITIVE_INFINITY)。
  • 通过该方法认为
  • 0.0d大于-0.0d。

您可能会发现 article 非常有用

如果您需要,可以查看

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

答案 2 :(得分:1)

您可以尝试这样的事情(未经测试):

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;                        
}

&#34;位&#34;通常是1到4左右,具体取决于你想要截止的精确程度。

一个改进是在屏蔽之前将第一个位的位置加1(对于&#34;舍入&#34;),但是你必须担心一直到最重要的波纹位。