在 Java 中比较双精度值的相等性。

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25160375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 23:44:56  来源:igfitidea点击:

Comparing double values for equality in Java.

javadoubledouble-precision

提问by chr0mzie

I would like some advice from people who have more experience working with primitive doubleequality in Java. Using d1 == d2for two doubles d1and d2is not sufficient due to possible rounding errors.

我想从那些double在 Java 中处理原始平等方面有更多经验的人那里得到一些建议。使用d1 == d2了两个双打d1d2不充分,由于可能的舍入误差。

My questions are:

我的问题是:

  1. Is Java's Double.compare(d1,d2) == 0handling rounding errors to some degree? As explained in the 1.7 documentationit returns value 0if d1is numerically equal to d2. 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? Please see example below.

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

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

Below is a generic function for checking equality considering relative error. What value of deltawould you recommend to capture the majority of rounding errors from simple operations +,-,/,* operations?

下面是考虑相对误差的用于检查相等性的通用函数。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));
}

采纳答案by Henry

You could experiment with delta values in the order of 10-15but you will notice that some calculations give a larger rounding error. Furthermore, the more operations you make the larger will be the accumulated rounding error.

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

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 arithmeticcan 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.

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

回答by Rahul Tripathi

From the javadoc for compareTo

来自 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).
  • 0.0d is considered by this method to be greater than -0.0d.
  • 此方法认为 Double.NaN 等于自身并大于所有其他双精度值(包括 Double.POSITIVE_INFINITY)。
  • 此方法认为 0.0d 大于 -0.0d。

You may find this articlevery 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 */ }

回答by Hot Licks

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