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

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

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

比如:

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

似乎是浪费加工。

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


当前回答

在这个版本中,你可以检查,这些数字之间的差异并不比某些分数(比如,0.0001%)更大:

bool floatApproximatelyEquals(const float a, const float b) {
    if (b == 0.) return a == 0.; // preventing division by zero
    return abs(1. - a / b) < 1e-6;
}

请注意Sneftel关于浮动可能的分数限制的评论。

还要注意的是,它不同于使用绝对的epsilon的方法——这里你不需要担心“数量级”——数字可能是,比如说1e100,或者1e-100,它们总是会被一致地比较,而且你不必为每一种情况更新epsilon。

其他回答

你不能用一个固定的。根据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;
}

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

在https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon上找到了另一个有趣的实现

#include <cmath>
#include <limits>
#include <iomanip>
#include <iostream>
#include <type_traits>
#include <algorithm>



template<class T>
typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type
    almost_equal(T x, T y, int ulp)
{
    // the machine epsilon has to be scaled to the magnitude of the values used
    // and multiplied by the desired precision in ULPs (units in the last place)
    return std::fabs(x-y) <= std::numeric_limits<T>::epsilon() * std::fabs(x+y) * ulp
        // unless the result is subnormal
        || std::fabs(x-y) < std::numeric_limits<T>::min();
}

int main()
{
    double d1 = 0.2;
    double d2 = 1 / std::sqrt(5) / std::sqrt(5);
    std::cout << std::fixed << std::setprecision(20) 
        << "d1=" << d1 << "\nd2=" << d2 << '\n';

    if(d1 == d2)
        std::cout << "d1 == d2\n";
    else
        std::cout << "d1 != d2\n";

    if(almost_equal(d1, d2, 2))
        std::cout << "d1 almost equals d2\n";
    else
        std::cout << "d1 does not almost equal d2\n";
}

这取决于你想要的比较有多精确。如果您想对完全相同的数字进行比较,那么只需使用==。(除非你真的想要完全相同的数字,否则你几乎不会想这么做。)在任何一个不错的平台上,你都可以做到以下几点:

diff= a - b; return fabs(diff)<EPSILON;

因为晶圆厂往往很快。我说的快是指它基本上是一个位与,所以它最好快。

用于比较双精度和浮点数的整数技巧很好,但往往会使各种CPU管道更难有效处理。现在,由于使用堆栈作为频繁使用的值的临时存储区域,在某些有序架构上它肯定不会更快。(在乎的人可以去Load-hit-store。)

意识到这是一个老话题,但这篇文章是我发现的关于比较浮点数的最直接的文章之一,如果你想探索更多,它也有更详细的参考资料,它的主要站点涵盖了处理浮点数的完整范围的问题《浮点指南:比较》。

我们可以在浮点公差中找到一篇更实用的文章,并指出有绝对公差测试,在c++中归结为:

bool absoluteToleranceCompare(double x, double y)
{
    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon() ;
}

及相对耐量试验:

bool relativeToleranceCompare(double x, double y)
{
    double maxXY = std::max( std::fabs(x) , std::fabs(y) ) ;
    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon()*maxXY ;
}

文章指出,当x和y较大时,绝对检验失败;当x和y较小时,相对检验失败。假设绝对耐受性和相对耐受性是相同的,综合测试将是这样的:

bool combinedToleranceCompare(double x, double y)
{
    double maxXYOne = std::max( { 1.0, std::fabs(x) , std::fabs(y) } ) ;

    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon()*maxXYOne ;
}