比较两个双精度浮点数或两个浮点数最有效的方法是什么?

简单地这样做是不正确的:

bool CompareDoubles1 (double A, double B)
{
   return A == B;
}

比如:

bool CompareDoubles2 (double A, double B) 
{
   diff = A - B;
   return (diff < EPSILON) && (-diff < EPSILON);
}

似乎是浪费加工。

有人知道更聪明的浮点比较器吗?


当前回答

我使用以下函数进行浮点数比较:

bool approximatelyEqual(double a, double b)
{
  return fabs(a - b) <= ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * std::numeric_limits<double>::epsilon());
}

其他回答

下面是使用std::numeric_limits::epsilon()不是答案的证明——对于大于1的值它会失败:

证明我上面的评论:

#include <stdio.h>
#include <limits>

double ItoD (__int64 x) {
    // Return double from 64-bit hexadecimal representation.
    return *(reinterpret_cast<double*>(&x));
}

void test (__int64 ai, __int64 bi) {
    double a = ItoD(ai), b = ItoD(bi);
    bool close = std::fabs(a-b) < std::numeric_limits<double>::epsilon();
    printf ("%.16f and %.16f %s close.\n", a, b, close ? "are " : "are not");
}

int main()
{
    test (0x3fe0000000000000L,
          0x3fe0000000000001L);

    test (0x3ff0000000000000L,
          0x3ff0000000000001L);
}

运行产生如下输出:

0.5000000000000000 and 0.5000000000000001 are  close.
1.0000000000000000 and 1.0000000000000002 are not close.

请注意,在第二种情况下(1且仅大于1),两个输入值尽可能接近,并且仍然比较为不接近。因此,对于大于1.0的值,不妨只使用相等性测试。当比较浮点值时,固定的epsilon将无法保存您的数据。

与epsilon值进行比较是大多数人所做的(甚至是在游戏编程中)。

你应该稍微改变你的实现:

bool AreSame(double a, double b)
{
    return fabs(a - b) < EPSILON;
}

编辑:克里斯特在最近的一篇博客文章中添加了一堆关于这个主题的很棒的信息。享受。

在这个版本中,你可以检查,这些数字之间的差异并不比某些分数(比如,0.0001%)更大:

bool floatApproximatelyEquals(const float a, const float b) {
    if (b == 0.) return a == 0.; // preventing division by zero
    return abs(1. - a / b) < 1e-6;
}

请注意Sneftel关于浮动可能的分数限制的评论。

还要注意的是,它不同于使用绝对的epsilon的方法——这里你不需要担心“数量级”——数字可能是,比如说1e100,或者1e-100,它们总是会被一致地比较,而且你不必为每一种情况更新epsilon。

在c++中获取的可移植方法是

#include <limits>
std::numeric_limits<double>::epsilon()

然后比较函数变成

#include <cmath>
#include <limits>

bool AreSame(double a, double b) {
    return std::fabs(a - b) < std::numeric_limits<double>::epsilon();
}

我使用这个代码:

bool AlmostEqual(double v1, double v2)
    {
        return (std::fabs(v1 - v2) < std::fabs(std::min(v1, v2)) * std::numeric_limits<double>::epsilon());
    }