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

时间:2010-09-16 15:32:54

标签: java

以下是该程序的输出。

value is : 2.7755575615628914E-17
Double.compare with zero : 1
isEqual with zero : true

我的问题是,什么应该是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;
    }
}

6 个答案:

答案 0 :(得分:11)

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

bool fuzzyEquals(double a, double b)
{
    return abs(a - b) < eps * max(abs(a), abs(b));
}

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

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

重点是使用 relative 精度,而不是绝对精度。

答案 1 :(得分:8)

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

我还注意到isEqual名字很差 - 你需要像isNearlyEQual这样的东西。出于一个原因,人们相当合理地期望“平等”是可传递的。至少,您需要表达结果不再具有传递性的想法 - 即,如果您对isEqual的定义,isEqual(a, c)可能是错误的,即使isEqual(a, b)和{ {1}}都是真的。

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

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

答案 2 :(得分: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);
}

这会提供~5.55E-12,但如果您增加N,则可以获得您想要的任何级别的错误。

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

答案 3 :(得分:7)

isEqual中,有类似的内容:

epsilon = Math.max(Math.ulp(d0), Math.ulp(d1))
  

双值的ulp是它之间的正距离   浮点值和下一个更大的双值。   [1]

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

答案 4 :(得分:1)

您首先应该阅读https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

讨论了比较浮点数的各种方法:绝对容差,相对容差,ulp距离。它提供了一个相当不错的论据,即ulp检查是要走的路。这个案例取决于如果你想检查两个浮点数是否相同,你必须考虑可表示的浮点数之间的距离。换句话说,你应该检查这两个数字是否在彼此的e浮点数内。

算法在C中给出,但可以使用java.lang.Double#doubleToLongBitsjava.lang.Float#floatToIntBits转换为java,以实现从浮动到整数类型的转换。另外,用java&gt; 1.5有方法ulp(double) ulp(float)和java&gt; 1.6 nextUp(double) nextUp(float) nextAfter(double, double) nextAfter(float, float),可用于量化两个浮点数之间的差异。

答案 5 :(得分:1)

这里涉及两个概念:

  1. 机器精度单位:Double.ulp()
  2. 给定double d的机器精度:Double.ulp(d)
  3. 如果你致电Double.ulp(),你将获得机器精度单位,这是你可以从某个硬件平台获得的精度......无论这个定义是什么!

    如果您致电Double.ulp(d),您将获得double d的机器精度。换句话说,每个double d都有其特定的精度。这比前一段更有用。

    当您执行涉及级联计算的迭代时,您必须特别注意细节,即:当前计算中使用先前计算的结果时。这是因为错误在这些情况下累积,并且在某些情况下可能最终导致结果偏离它们应该提供的真实价值。在某些情况下,累积误差的大小甚至可能大于真实值。查看一些disastrous examples here

    在某些业务领域,数值计算错误根本不可接受。根据业务领域,法规,要求和特性,您必须采用其他方法来简化选择使用浮点运算(即:doublesfloats)。

    例如,在财务案例中,永远不会使用浮点运算。在处理金钱时,永远不要使用doublesfloats。决不。期。您可以根据具体情况使用BigDecimal或fixed point arithmetic

    在处理股票价格的特定情况下,您知道价格总是精确到5位数,在这种情况下,fixed point arithmetic足够多,并且还提供了您可能获得的最大性能,这是一个这个业务领域非常强大和普遍的要求。

    如果业务领域确实需要数值计算,那么在这种情况下,您必须确保在严格且非常小心的控制下保持错误传播。这是一个很长的主题,有很多技术,很多时候开发人员忽略了这个问题,只是认为对一个为他们做了所有艰苦工作的方法有一个神奇的调用。不,它没有。你必须做你的研究,做你的功课,并做所有必要的努力工作,以确保你控制错误。您需要准确了解您实施的数值算法的确切情况。