简体   繁体   English

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

[英]Comparing double values for equality in Java.

I would like some advice from people who have more experience working with primitive double equality in Java. 我想从那些在Java中使用原始double相等经验的人那里得到一些建议。 Using d1 == d2 for two doubles d1 and d2 is not sufficient due to possible rounding errors. 由于可能的舍入误差,将d1 == d2用于两个双精度d1d2是不够的。

My questions are: 我的问题是:

  1. Is Java's Double.compare(d1,d2) == 0 handling rounding errors to some degree? Java的Double.compare(d1,d2) == 0在某种程度上处理舍入错误吗? As explained in the 1.7 documentation it returns value 0 if d1 is numerically equal to d2 . 1.7文档中所述,如果d1在数值上等于d2则返回值0 Is anyone certain what exactly they mean by numerically equal? 有人确定它们在数值上是什么意思吗?

  2. Using relative error calculation against some delta value, is there a generic (not application specific) value of delta you would recommend? 对某些delta值使用相对误差计算,是否会推荐使用delta的通用(非特定于应用程序)值? Please see example below. 请参阅下面的示例。

Below is a generic function for checking equality considering relative error. 下面是考虑相对误差来检查相等性的通用函数。 What value of delta would you recommend to capture the majority of rounding errors from simple operations +,-,/,* operations? 您建议从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));
}

You could experiment with delta values in the order of 10 -15 but you will notice that some calculations give a larger rounding error. 您可以尝试大约10到15的增量值,但您会注意到某些计算会产生更大的舍入误差。 Furthermore, the more operations you make the larger will be the accumulated rounding error. 此外,您所做的操作越多,将是累积的舍入误差。

One particularly bad case is if you subtract two almost equal numbers, for example 1.0000000001 - 1.0 and compare the result to 0.0000000001 一个特别糟糕的情况是,如果你减去两个几乎相等的数字,例如1.0000000001 - 1.0并将结果与​​0.0000000001进行比较

So there is little hope to find a generic method that would be applicable in all situations. 因此,没有希望找到适用于所有情况的通用方法。 You always have to calculate the accuracy you can expect in a certain application and then consider results equal if they are closer than this accuracy. 您总是必须计算在某个应用程序中可以预期的精度,然后如果它们比这个精度更接近则认为结果相等。

For example the output of 例如,输出

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

}

is

1.7347234759768068E-16
8.274036411668976E-8

Interval arithmetic can be used to keep track of the accumulated rounding errors. 区间运算可用于跟踪累积的舍入误差。 However in practise the error intervals come out too pessimistic, because sometimes rounding errors also cancel each other. 然而在实践中,错误间隔过于悲观,因为有时舍入错误也会相互抵消。

From the javadoc for compareTo 来自于比较的javadoc

  • Double.NaN is considered by this method to be equal to itself and greater than all other double values (including Double.POSITIVE_INFINITY). Double.NaN被此方法视为等于其自身并且大于所有其他double值(包括Double.POSITIVE_INFINITY)。
  • 0.0d is considered by this method to be greater than -0.0d. 该方法认为0.0d大于-0.0d。

You may find this article very helpful 您可能会发现这篇文章非常有帮助

If you want you can check like 如果你想要你可以检查一下

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

You could try something like this (not tested): 你可以试试这样的东西(未经测试):

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

"bits" would typically be from 1 to 4 or so, depending on how precise you wanted the cutoff. “位”通常为1到4左右,具体取决于您希望截止的精确程度。

A refinement would be to add 1 to the position of the first bit to be zeroed before masking (for "rounding"), but then you have to worry about ripple all the way up past the most significant bit. 一个改进是在屏蔽之前将第一个位的位置加1(用于“舍入”),但是你必须担心纹波一直超过最高位。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM