有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

在Java中比较相等的双值。

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

我的问题是:

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

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

共 (3) 个答案

  1. # 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;                        
    }
    

    “位”通常是1到4左右,这取决于你想要的截止值有多精确

    一种改进方法是在掩蔽(用于“舍入”)之前,将第一个要归零的位的位置加上1,但随后必须担心经过最高有效位后的纹波

  2. # 2 楼答案

    来自javadoc for compareTo

    • 加倍。此方法认为NaN等于自身,大于所有其他双精度值(包括double.POSITIVE_∞)
    • 此方法认为0.0d大于-0.0d

    你可能会发现这很有帮助

    如果你想的话,你可以像

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

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

    一种特别糟糕的情况是,如果减去两个几乎相等的数字,例如1.0000000001-1.0,然后将结果与0.0000000001进行比较

    因此,几乎没有希望找到一种适用于所有情况的通用方法。在一定的应用程序中,你必须计算出你所期望的精度,然后如果结果比这个精度更接近,则认为结果是相等的。p>

    例如

    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可用于跟踪累积的舍入误差。然而,在实践中,误差间隔过于悲观,因为有时舍入误差也会相互抵消