【问题标题】:Comparing double values for equality in Java.在 Java 中比较 double 值是否相等。
【发布时间】:2014-09-29 09:18:40
【问题描述】:

我想向那些在 Java 中使用原始 double 平等有更多经验的人提出一些建议。由于可能存在舍入错误,将 d1 == d2 用于两个双精度 d1d2 是不够的。

我的问题是:

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

  2. 对某个 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));
}

【问题讨论】:

标签: java double double-precision


【解决方案1】:

您可以尝试 10-15 的增量值,但您会注意到某些计算会产生较大的舍入误差。此外,您进行的操作越多,累积的舍入误差越大。

一个特别糟糕的情况是,如果您减去两个几乎相等的数字,例如 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 可用于跟踪累积的舍入误差。然而在实践中,误差间隔过于悲观,因为有时舍入误差也会相互抵消。

【讨论】:

  • 如果期望值为 0.0 怎么办?
  • @simon.watts 没什么特别的,正如我上面所说的:“几乎没有希望找到一种适用于所有情况的通用方法”。
【解决方案2】:

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

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(用于“舍入”),但是您必须担心一直到最高有效位的波纹。

【讨论】:

  • 谢谢,这是我一直在寻找的方法。但是,这在“跨 0”时并不完全有效——正 +ε 和负 -ε 并不被视为接近。这是通过我所做测试的替代版本(但如果它在某处也有一些小故障,我不会感到震惊):return d1==d2 /* hotpath; also handles infinities and NaNs. */ || (Math.abs(d1-d2) &lt; Math.max( Math.ulp(d1), Math.ulp(d2) ) * (0b1L &lt;&lt; bits));
【解决方案3】:

来自 compareTo 的 javadoc

  • 此方法认为 Double.NaN 等于自身,并且大于所有其他双精度值(包括 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 */ }

【讨论】:

    猜你喜欢
    • 2012-02-16
    • 2016-10-20
    • 1970-01-01
    • 2015-03-13
    • 2015-09-16
    • 1970-01-01
    • 2012-10-03
    • 2011-10-10
    相关资源
    最近更新 更多