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

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

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

比如:

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

似乎是浪费加工。

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


当前回答

'返回fabs(a - b) < EPSILON;

这是可以的,如果:

输入的数量级变化不大 极少数相反的符号可以被视为相等

否则就会给你带来麻烦。双精度数的分辨率约为小数点后16位。如果您正在比较的两个数字在量级上大于EPSILON*1.0E16,那么您可能会说:

return a==b;

我将研究一种不同的方法,假设您需要担心第一个问题,并假设第二个问题对您的应用程序很好。解决方案应该是这样的:

#define VERYSMALL  (1.0E-150)
#define EPSILON    (1.0E-8)
bool AreSame(double a, double b)
{
    double absDiff = fabs(a - b);
    if (absDiff < VERYSMALL)
    {
        return true;
    }

    double maxAbs  = max(fabs(a) - fabs(b));
    return (absDiff/maxAbs) < EPSILON;
}

这在计算上是昂贵的,但有时是需要的。这就是我们公司必须做的事情,因为我们要处理一个工程库,输入可能相差几十个数量级。

无论如何,关键在于(并且适用于几乎所有的编程问题):评估你的需求是什么,然后想出一个解决方案来满足你的需求——不要认为简单的答案就能满足你的需求。如果在您的评估后,您发现fabs(a-b) < 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();
}

General-purpose comparison of floating-point numbers is generally meaningless. How to compare really depends on a problem at hand. In many problems, numbers are sufficiently discretized to allow comparing them within a given tolerance. Unfortunately, there are just as many problems, where such trick doesn't really work. For one example, consider working with a Heaviside (step) function of a number in question (digital stock options come to mind) when your observations are very close to the barrier. Performing tolerance-based comparison wouldn't do much good, as it would effectively shift the issue from the original barrier to two new ones. Again, there is no general-purpose solution for such problems and the particular solution might require going as far as changing the numerical method in order to achieve stability.

下面是使用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将无法保存您的数据。

我使用这个代码:

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

比较浮点数取决于上下文。因为即使改变操作的顺序也会产生不同的结果,所以知道你希望这些数字有多“相等”是很重要的。

在研究浮点数比较时,比较Bruce Dawson编写的浮点数是一个很好的开始。

以下定义来自Knuth的《The art of computer programming》:

bool approximatelyEqual(float a, float b, float epsilon)
{
    return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool essentiallyEqual(float a, float b, float epsilon)
{
    return fabs(a - b) <= ( (fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool definitelyGreaterThan(float a, float b, float epsilon)
{
    return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool definitelyLessThan(float a, float b, float epsilon)
{
    return (b - a) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

当然,选择取决于上下文,并决定你想要的数字有多相等。

比较浮点数的另一种方法是查看数字的ULP(最后位置的单位)。虽然没有专门处理比较,但“每个计算机科学家都应该知道浮点数”这篇论文是了解浮点数如何工作以及陷阱是什么,包括什么是ULP的很好的资源。