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

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

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

比如:

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

似乎是浪费加工。

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


当前回答

这个怎么样?

template<typename T>
bool FloatingPointEqual( T a, T b ) { return !(a < b) && !(b < a); }

我见过各种方法,但从来没有见过这个,所以我也很好奇听到任何评论!

其他回答

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

在研究浮点数比较时,比较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的很好的资源。

有关更深入的方法,请参阅比较浮点数。以下是该链接的代码片段:

// Usable AlmostEqual function    
bool AlmostEqual2sComplement(float A, float B, int maxUlps)    
{    
    // Make sure maxUlps is non-negative and small enough that the    
    // default NAN won't compare as equal to anything.    
    assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024);    
    int aInt = *(int*)&A;    
    // Make aInt lexicographically ordered as a twos-complement int    
    if (aInt < 0)    
        aInt = 0x80000000 - aInt;    
    // Make bInt lexicographically ordered as a twos-complement int    
    int bInt = *(int*)&B;    
    if (bInt < 0)    
        bInt = 0x80000000 - bInt;    
    int intDiff = abs(aInt - bInt);    
    if (intDiff <= maxUlps)    
        return true;    
    return false;    
}

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

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

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

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

以更一般的方式:

template <typename T>
bool compareNumber(const T& a, const T& b) {
    return std::abs(a - b) < std::numeric_limits<T>::epsilon();
}

注意: 正如@SirGuy所指出的,这种方法是有缺陷的。 我把这个答案留在这里,作为一个不遵循的例子。

我对任何涉及浮点减法的答案都非常谨慎(例如,fabs(a-b) < epsilon)。首先,浮点数在更大的量级上变得更稀疏,在足够大的量级上,当间隔大于时,您可能只需要做a == b。其次,减去两个非常接近的浮点数(因为您正在寻找接近相等的浮点数)正是您得到灾难性抵消的方式。

虽然不能移植,但我认为grom的答案在避免这些问题方面做得最好。