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

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

我的问题是:

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

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

下面是考虑相对误差来检查相等性的通用函数。 您建议从delta操作+, – ,/,*操作中捕获大部分舍入误差的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的 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 

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

来自于比较的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 */ } 

你可以试试这样的东西(未经测试):

 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(用于“舍入”),但是你必须担心纹波一直超过最高位。