【问题标题】:C++ Comparison of doubles using an epsilonC++ 使用 epsilon 比较双精度
【发布时间】:2018-06-05 05:58:43
【问题描述】:

我为使用 epsilon 的双精度定义了一个比较函数,如下所示:What is the most effective way for float and double comparison?。如果它们的绝对差小于 epsilon,则两个双精度数不同。

如果我有双打 d1 和 d2,d2 = d1+epsilon,d1 不应该等于 d2,因为它们的差异是 epsilon。对吧?

它适用于某些值,但并非总是如此。它可能与架构或编译器有关?我应该怎么做才能改进我的比较功能?

#include <iomanip>  // For std::setprecision
#include <iostream>
#include "math.h"   // For fabs

const double epsilon = 0.001;

bool equal(const double &d1, const double &d2)
{
    return fabs(d1-d2) < epsilon;
}

int main()
{
    double d1 = 3.5;
    double d2 = d1+epsilon;

    // d2 = d1 + epsilon should not be equal to d1.
    std::cout << std::setprecision(16) << "d1 = " << d1 << " d2 = " << d2 << std::endl;
    bool eq = equal(d1,d2);
    if (eq)
    {
        std::cout << "They should not be equal, but they are!" << std::endl;
    }
    else
    {
        std::cout << "They are not equal. OK!" << std::endl;
    }
}

我的机器上的输出:

d1 = 3.5 d2 = 3.501

他们不应该相等,但他们是!

【问题讨论】:

  • "如果我有双打 d1 和 d2,其中 d2 = d1+epsilon,d1 不应该等于 d2,因为它们相差 epsilon。对吧?"不,完全一样的原因。
  • 作为旁注,std::setprecision 流修饰符不会修改内置类型精度。您不能修改内置类型精度。
  • 你没看到讽刺吗?您使用 epsilon 比较来解决浮点运算不精确的事实 - 然后期望涉及 epsilon 的表达式的精确结果。 d1+epsilon 的精确数学值在 double 中无法表示,因此 d2 最终会稍微小一些。

标签: c++ precision


【解决方案1】:

让我们调用pe 精度错误。对于 IEEE 754 标准binary64,它是无穷小的(~2^−53

数学是: [伪代码]

d1 = 3.5 + pe1
d2 = 3.501 + pe2

d2 - d1 = 0.001 + pe2 - pe1

epsilon = 0.001 + pe3

表达式的结果: [伪代码]

|d1-d2| < epsilon  //Since you know that d2>d1 you can replace |d1-d2| by d2-d1
0.001 + pe2 - pe1 < 0.001 + pe3
pe2 - pe1 < pe3

这种边界情况可以是truefalse,取决于pe3pe2pe1 的大小和符号。但还没有确定。

【讨论】:

    【解决方案2】:

    这里实际上有两个不同的问题。首先,您的比较应该是 >=,而不是 >,因此它“适用”于差异正好是 epsilon 的测试用例。

    第二个(潜在的)问题是,在一般情况下,您不能真正使用常量 epsilon 进行此类比较。您需要根据所涉及数字的预期大小以及计算中预期的累积数值误差来选择 epsilon。

    浮点数存储的值越大,精度越低。如果你用一个“足够大”的值重复你的测试,它每次都会失败。

    【讨论】:

      【解决方案3】:

      如果我有双打 d1 和 d2,d2 = d1+epsilon,d1 不应该等于 d2,因为它们的差异是 epsilon。对吧?

      错了。你的逻辑被打破了。你已经告诉过你不应该在没有 epsilon 的情况下比较双打,但下一步你会惊讶于 fabs(d1-d2) == epsilon 不是真的。

      我应该怎么做才能提高我的比较功能?

      没什么,d1 + epsilon 是一个边界情况,您无法确定该数字是否等于d1。这是浮点运算的缺点。

      【讨论】:

        【解决方案4】:

        最常见的浮点警告之一:如果将一个数字放入浮点,并不意味着它将存储为完全相同的值。相反,它们会被稍微修改以创建一个最接近您预期值的二进制结构。

        好的,回到正题!尝试将此添加到您的源代码中:

        std::cout << fabs(d1-d2) << std::endl;
        

        并找到如下输出:)

        0.0009999999999998899

        你看到了吗?它显然小于您的 epsilon。

        【讨论】:

        • 我预计不会出现精度问题,因为与双精度的系统精度 (~1e-16) 相比,epsilon (0.001) 的值“很大”,显然这是一厢情愿。
        • 这与系统精度无关。浮点数只是编码值的有限数量的位。显然,浮点值的数量是不可数的,而位组合的数量是可数的。因此,任何常规浮点格式(float、double 等)都是损失驱动的。所以,试着这样想:浮点数是一种 JPEG(无论如何你都会得到基于存储的工件),而整数是 PNG :)
        猜你喜欢
        • 2012-11-21
        • 2015-12-17
        • 2023-03-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多