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

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

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

比如:

bool CompareDoubles2 (double A, double B) 
{
   diff = A - B;
   return (diff < EPSILON) && (-diff < 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的圆,则认为两者相等。

其他回答

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

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

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

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

正如其他人所指出的那样,使用固定指数(例如0.0000001)对于远离该值的值是无用的。例如,如果你的两个值是10000.000977和10000,那么这两个数字之间没有32位浮点值——10000和10000.000977是你可能得到的最接近的值,而不是位对位相同。这里,小于0.0009是没有意义的;你也可以使用直接等式运算符。

同样地,当两个值的大小接近ε时,相对误差增长到100%。

Thus, trying to mix a fixed point number such as 0.00001 with floating-point values (where the exponent is arbitrary) is a pointless exercise. This will only ever work if you can be assured that the operand values lie within a narrow domain (that is, close to some specific exponent), and if you properly select an epsilon value for that specific test. If you pull a number out of the air ("Hey! 0.00001 is small, so that must be good!"), you're doomed to numerical errors. I've spent plenty of time debugging bad numerical code where some poor schmuck tosses in random epsilon values to make yet another test case work.

如果你从事任何类型的数值编程,并认为你需要达到定点的epsilon,请阅读BRUCE关于比较浮点数的文章。

浮点数比较

你不能用一个固定的。根据double的值,EPSILON会发生变化。

更好的双比较应该是:

bool same(double a, double b)
{
  return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b
    && std::nextafter(a, std::numeric_limits<double>::max()) >= b;
}

这是另一个解:

#include <cmath>
#include <limits>

auto Compare = [](float a, float b, float epsilon = std::numeric_limits<float>::epsilon()){ return (std::fabs(a - b) <= epsilon); };

我为java编写这篇文章,但是您可能会发现它很有用。它使用长变量而不是双变量,但会处理nan、亚法线等。

public static boolean equal(double a, double b) {
    final long fm = 0xFFFFFFFFFFFFFL;       // fraction mask
    final long sm = 0x8000000000000000L;    // sign mask
    final long cm = 0x8000000000000L;       // most significant decimal bit mask
    long c = Double.doubleToLongBits(a), d = Double.doubleToLongBits(b);        
    int ea = (int) (c >> 52 & 2047), eb = (int) (d >> 52 & 2047);
    if (ea == 2047 && (c & fm) != 0 || eb == 2047 && (d & fm) != 0) return false;   // NaN 
    if (c == d) return true;                            // identical - fast check
    if (ea == 0 && eb == 0) return true;                // ±0 or subnormals
    if ((c & sm) != (d & sm)) return false;             // different signs
    if (abs(ea - eb) > 1) return false;                 // b > 2*a or a > 2*b
    d <<= 12; c <<= 12;
    if (ea < eb) c = c >> 1 | sm;
    else if (ea > eb) d = d >> 1 | sm;
    c -= d;
    return c < 65536 && c > -65536;     // don't use abs(), because:
    // There is a posibility c=0x8000000000000000 which cannot be converted to positive
}
public static boolean zero(double a) { return (Double.doubleToLongBits(a) >> 52 & 2047) < 3; }

请记住,在一些浮点运算之后,number可能与我们期望的非常不同。没有代码可以解决这个问题。