执行双值相等比较时,epsilon值应该是多少

Che*_*eng 21 java

这是以下程序的输出.

value is : 2.7755575615628914E-17
Double.compare with zero : 1
isEqual with zero : true
Run Code Online (Sandbox Code Playgroud)

我的问题是,什么应该是epsilon值?是否有任何有力的方法来获得价值,而不是从天空中挑选一个数字.


package sandbox;

/**
 *
 * @author yccheok
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        double zero = 1.0/5.0 + 1.0/5.0 - 1.0/10.0 - 1.0/10.0 - 1.0/10.0 - 1.0/10.0;
        System.out.println("value is : " + zero);
        System.out.println("Double.compare with zero : " + Double.compare(zero, 0.0));
        System.out.println("isEqual with zero : " + isEqual(zero, 0.0));
    }

    public static boolean isEqual(double d0, double d1) {
        final double epsilon = 0.0000001;
        return d0 == d1 ? true : Math.abs(d0 - d1) < epsilon;
    }
}
Run Code Online (Sandbox Code Playgroud)

Ale*_* C. 12

我喜欢(伪代码,我不做java)

bool fuzzyEquals(double a, double b)
{
    return abs(a - b) < eps * max(abs(a), abs(b));
}
Run Code Online (Sandbox Code Playgroud)

与epsilon是机器epsilon的几倍.如果您不知道该使用什么,请取10 ^ -12.

然而,这非常依赖于问题.如果给出a和b的计算容易出现舍入误差,或者涉及许多操作,或者它们本身处于某种(已知的)准确度之内,那么你需要采用更大的epsilon.

重点是使用相对精度,而不是绝对精度.


Jer*_*fin 8

没有一个正确的价值.您需要相对于所涉及的数字的大小来计算它.你基本上处理的是一些有效数字,而不是具体的数字.例如,如果您的数字都在1e-100范围内,并且您的计算应保持大致8位有效数字,那么您的epsilon应该在1e-108左右.如果你对1e + 200范围内的数字做了相同的计算,那么你的epsilon将在1e + 192左右(即epsilon~ = magnitude - 有效数字).

我还注意到这isEqual是一个很糟糕的名字 - 你想要的东西isNearlyEQual.出于一个原因,人们相当合理地期望"平等"是可传递的.至少,你需要传达结果不再具有传递性的想法 - 即,你的定义,即使并且都是真的isEqual,isEqual(a, c)也可能是假的.isEqual(a, b)isEqual(b, c)

编辑:(回应评论):我说"如果[...]你的计算应保持大致8位有效数字,那么你的epsilon应该......".基本上,它涉及到你正在做什么计算,以及你在这个过程中可能失去多少精确度,以提供一个合理的猜测,在重要之前差异有多大.在不知道你正在做的计算的情况下,我无法猜测.

就epsilon的大小而言:不,它总是小于或等于1 是没有意义的.浮点数只能保持有限的精度.在IEEE双精度浮点的情况下,可以表示的最大精度是大约20个十进制数字.这意味着如果开始时1E + 200,从该数字机器可表示绝对最小差在所有为约1E + 180(和双可以表示数字高达〜1E + 308,在该点的最小差可以表示为~1e + 288).


mob*_*mob 8

你的第二个问题的答案是否定的.有限机器精度误差的大小可以任意大:

public static void main(String[] args) {
    double z = 0.0;
    double x = 0.23;
    double y = 1.0 / x;
    int N = 50000;
    for (int i = 0; i < N; i++) {
        z += x * y - 1.0;
    }
    System.out.println("z should be zero, is " + z);
}
Run Code Online (Sandbox Code Playgroud)

这给了~5.55E-12,但如果你增加,N你可以得到你想要的任何级别的错误.

关于如何编写数值稳定的算法,有大量过去和现在的研究.这是一个难题.


Ahm*_*sih 8

isEqual,有类似的东西:

epsilon = Math.max(Math.ulp(d0), Math.ulp(d1))
Run Code Online (Sandbox Code Playgroud)

双值的ulp是该浮点值与接下来幅度较大的双值之间的正距离.[1]

[1] http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html#ulp%28double%29