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

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

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

比如:

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

似乎是浪费加工。

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


当前回答

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

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

bool AreSame(double a, double b)
{
    return 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();
}
/// testing whether two doubles are almost equal. We consider two doubles
/// equal if the difference is within the range [0, epsilon).
///
/// epsilon: a positive number (supposed to be small)
///
/// if either x or y is 0, then we are comparing the absolute difference to
/// epsilon.
/// if both x and y are non-zero, then we are comparing the relative difference
/// to epsilon.
bool almost_equal(double x, double y, double epsilon)
{
    double diff = x - y;
    if (x != 0 && y != 0){
        diff = diff/y; 
    }

    if (diff < epsilon && -1.0*diff < epsilon){
        return true;
    }
    return false;
}

我在我的小项目中使用了这个函数,它是有效的,但注意以下几点:

双精度误差可以为你制造惊喜。假设epsilon = 1.0e-6,那么根据上面的代码,1.0和1.000001不应该被认为是相等的,但在我的机器上,函数认为它们是相等的,这是因为1.000001不能精确地转换为二进制格式,它可能是1.0000009xxx。我用1.0和1.0000011测试了它,这次我得到了预期的结果。

你写的代码有bug:

return (diff < EPSILON) && (-diff > EPSILON);

正确的代码应该是:

return (diff < EPSILON) && (diff > -EPSILON);

(…是的,这是不同的)

我想知道晶圆厂是否会让你在某些情况下失去懒惰的评价。我会说这取决于编译器。你可能想两种都试试。如果它们在平均水平上是相等的,则采用晶圆厂实现。

如果你有一些关于两个浮点数中哪一个比另一个更大的信息,你可以根据比较的顺序来更好地利用惰性求值。

最后,通过内联这个函数可能会得到更好的结果。不过不太可能有太大改善……

编辑:OJ,谢谢你纠正你的代码。我相应地删除了我的评论

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

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

使用任何其他建议都要非常小心。这完全取决于上下文。

我花了很长时间在一个系统中追踪错误,该系统假设|a-b|<epsilon,则a==b。潜在的问题是:

The implicit presumption in an algorithm that if a==b and b==c then a==c. Using the same epsilon for lines measured in inches and lines measured in mils (.001 inch). That is a==b but 1000a!=1000b. (This is why AlmostEqual2sComplement asks for the epsilon or max ULPS). The use of the same epsilon for both the cosine of angles and the length of lines! Using such a compare function to sort items in a collection. (In this case using the builtin C++ operator == for doubles produced correct results.)

就像我说的,这完全取决于上下文和a和b的预期大小。

顺便说一下,std::numeric_limits<double>::epsilon()是“机器epsilon”。它是1.0和下一个用double表示的值之间的差值。我猜它可以用在比较函数中,但只有当期望值小于1时。(这是对@cdv的回答的回应…)

同样,如果你的int算术是双精度的(这里我们在某些情况下使用双精度来保存int值),你的算术是正确的。例如,4.0/2.0将等同于1.0+1.0。只要你不做导致分数(4.0/3.0)的事情,或者不超出int的大小。