【问题标题】:What should be the epsilon value when performing double value equal comparison执行双值相等比较时,epsilon 值应该是多少
【发布时间】:2010-09-16 15:32:53
【问题描述】:

这是下面程序的输出。

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

【问题讨论】:

  • @Cheok,你的问题不清楚,你对 epsilon 有什么期望?

标签: java


【解决方案1】:

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

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

epsilon 是机器 epsilon 的几倍。不知道用什么就拿 10^-12。

然而,这完全取决于问题。如果给出 a 和 b 的计算容易出现舍入误差,或者涉及许多操作,或者它们本身在某个(已知)精度范围内,那么您需要采用更大的 epsilon。

这点是使用相对精度,而不是绝对的。

【讨论】:

  • 我可以返回 abs(a - b)
  • 我认为我们应该比较 abs(a) 和 abs(b),并取最小值与 eps 相乘。基本见Equal:jstock.cvs.sourceforge.net/viewvc/jstock/jstock/src/org/yccheok/…
  • @Yan:这取决于你想要什么。
  • @AlexandreC.,@CheokYanCheng,你能详细说明一下吗? a 非常接近 0 并且 b 恰好为 0 时不会使用最小值永远不会返回 true?
  • @clausavram:这是“取决于你想要什么”部分。使用最小值将暗示你所说的,它可能是你想要的。在实践中,当评估我不知道大小的数字之间的距离时,我总是使用abs(b - a) / max(abs(a), abs(b))(对于a == b == 0的情况也有保护)。
【解决方案2】:

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

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,你可以获得几乎任何你想要的错误级别。

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

【讨论】:

  • 不,只是 x * (1.0/x) 不完全等于 1 的数字示例。
  • 我们不应该将 0.23 称为magic number 吗?因为我也可以使用 0.24,对吗?我想如果我们可以选择一个任意数字,我们通常将其称为magic number
【解决方案3】:

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

我还要注意 isEqual 是一个糟糕的名字——你想要像 isNearlyEQual 这样的名字。出于一个原因,人们相当合理地期望“equal”具有传递性。至少,您需要传达结果不再具有传递性的想法——即,您对isEqual 的定义,isEqual(a, c) 可能为假,即使isEqual(a, b)isEqual(b, c) 都为真。

编辑:(回应 cmets):我说“如果 [...] 你的计算应该保持大约 8 个有效数字,那么你的 epsilon 应该是......”。基本上,它涉及查看您正在执行的计算,以及您在此过程中可能会损失多少精度,以提供一个合理的猜测,即在差异显着之前必须有多大的差异。不知道你正在做的计算,我猜不出来。

就 epsilon 的大小而言:不,它确实总是小于或等于 1。浮点数只能保持有限的精度。在 IEEE 双精度浮点的情况下,可以表示的最大精度约为 20 位十进制数字。这意味着如果你从 1e+200 开始,与机器可以表示的数字的绝对最小差异根本大约是 1e+180(双精度可以表示高达 ~1e+308 的数字,此时可以表示的最小差异为~1e+288)。

【讨论】:

  • 为什么我的数字是 1e-100,那么 epsilon 应该在 1e-108 左右。为什么是 8 个有效数字?
  • @Coffin,为什么 epsilon 是 1e+192 ? epsilon 不应该至少小于 1 且大于 0 吗?
【解决方案4】:

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

【讨论】:

    【解决方案5】:

    这里涉及到两个概念:

    1. 机器精度单位:Double.ulp()
    2. 给定double d 的机器精度:Double.ulp(d)

    如果您调用Double.ulp(),您将获得机器精度单位,这是您可以期望从某个硬件平台获得的精度......不管这个定义是什么!

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

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

    在某些业务领域,数值计算错误是不可接受的。根据业务领域、其法规、要求和特征,您必须采用替代方法来简单地选择使用浮点运算(即:doublesfloats)。

    以金融为例,永远不要使用浮点运算。在处理金钱时,永远不要使用doublesfloats。绝不。时期。您可以根据具体情况使用 BigDecimal 或 fixed point arithmetic

    在处理股票价格的特定情况下,您知道价格总是有 5 位精度,在这种情况下,fixed point arithmetic 已经足够了,并且还提供了您可能获得的最大性能,这是一个非常强大的和该业务领域的共同需求。

    如果业务领域确实需要数值计算,那么在这种情况下,您必须确保将错误传播保持在严格且非常谨慎的控制之下。这是一个很长的主题,有很多技术,而且开发人员经常忽略这个问题,只是认为有一个对方法的魔术调用可以为他们完成所有艰苦的工作。不,它没有。你必须做你的研究,做你的功课,做所有必要的艰苦工作,以确保你控制错误。您需要准确了解您已实现的数值算法发生了什么。

    【讨论】:

      【解决方案6】:

      您绝对应该先阅读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 > 1.5,还有一些方法 ulp(double) ulp(float) 和对于 java > 1.6 nextUp(double) nextUp(float) nextAfter(double, double) nextAfter(float, float),它们对于量化两个浮点数之间的差异很有用。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-20
      • 2018-03-21
      • 2018-06-05
      • 1970-01-01
      • 2021-04-15
      • 2014-09-29
      • 2012-02-16
      • 2022-01-05
      相关资源
      最近更新 更多