C ++使用epsilon的双打比较

时间:2017-12-22 15:31:59

标签: c++ precision

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

如果我有d1和d2加倍,d2 = d1 + epsilon,d1不应等于d2,因为它们相差ε。正确?

它适用于某些值,但不是所有时间。它可能与架构或编译器有关?我该怎么做才能改善比较功能?

#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

     

他们不应该是平等的,但他们是!

4 个答案:

答案 0 :(得分:2)

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

好的,回到商界!尝试将其添加到源代码中:

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

找到输出如下:)

  

0.0009999999999998899

你知道吗?它明显少于你的epsilon。

答案 1 :(得分:1)

  

如果我有d1和d2加倍,d2 = d1 + epsilon,d1不应等于d2,因为它们相差ε。正确?

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

  

我应该怎样做才能改善比较功能?

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

答案 2 :(得分:0)

这里实际上有两个不同的问题。首先,你的比较应该是&gt; =而不是&gt;,所以它“适用于”你的测试用例,其差异恰好是epsilon。

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

浮点数不太精确,它们存储的值的大小越大。如果您使用“足够大”的值重复测试,则每次都会失败。

答案 3 :(得分:0)

让我们调用pe精度错误。对于IEEE 754标准~2^−53

,它是无穷小的(binary64

数学是: [伪代码]

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的幅度和符号。但它尚未确定。