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

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

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

比如:

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

似乎是浪费加工。

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


当前回答

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

我花了很长时间在一个系统中追踪错误,该系统假设|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的大小。

其他回答

Why not perform bitwise XOR? Two floating point numbers are equal if their corresponding bits are equal. I think, the decision to place the exponent bits before mantissa was made to speed up comparison of two floats. I think, many answers here are missing the point of epsilon comparison. Epsilon value only depends on to what precision floating point numbers are compared. For example, after doing some arithmetic with floats you get two numbers: 2.5642943554342 and 2.5642943554345. They are not equal, but for the solution only 3 decimal digits matter so then they are equal: 2.564 and 2.564. In this case you choose epsilon equal to 0.001. Epsilon comparison is also possible with bitwise XOR. Correct me if I am wrong.

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

我花了很长时间在一个系统中追踪错误,该系统假设|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的大小。

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

我使用这个代码。不像上面的答案,这允许一个人 给出一个在代码注释中解释的abs_relative_error。

第一个版本比较复数,使错误 可以用两个矢量之间的夹角来解释 在复平面上具有相同的长度(这给出了一点 洞察力)。然后是2实数的正确公式 数字。

https://github.com/CarloWood/ai-utils/blob/master/almost_equal.h

后者是

template<class T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
   almost_equal(T x, T y, T const abs_relative_error)
{
  return 2 * std::abs(x - y) <= abs_relative_error * std::abs(x + y);
}

其中abs_relative_error基本上(两倍)是文献中最接近定义的绝对值:相对错误。但这只是名字的选择。

我认为在复平面中最明显的是。如果|x| = 1, y在x周围形成一个直径为abs_relative_error的圆,则认为两者相等。

在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();
}