如何正确和标准地比较浮动?

Dmi*_*riy 28 c++ floating-point floating-accuracy double-precision

每次我开始一个新项目,当我需要比较一些浮点数或双变量时,我会像这样编写代码:

if (fabs(prev.min[i] - cur->min[i]) < 0.000001 &&
    fabs(prev.max[i] - cur->max[i]) < 0.000001) {
        continue;
}
Run Code Online (Sandbox Code Playgroud)

然后我想摆脱这些神奇的变量0.000001(和双倍的0.00000000001)和fabs,所以我写了一个内联函数,有些定义:

#define FLOAT_TOL 0.000001
Run Code Online (Sandbox Code Playgroud)

所以我想知道是否有任何标准方法可以做到这一点?可能是一些标准的头文件?浮点数和双限制(最小值和最大值)也很好

Mic*_*rdt 17

浮点指南:

这是一个糟糕的方法,因为选择一个固定的epsilon,因为它"看起来很小"实际上当被比较的数字非常小时实际上太大了.对于完全不同的数字,比较将返回"true".当数字非常大时,epsilon可能最终小于最小的舍入误差,因此比较总是返回"false".

这里的"神奇数字"的问题不在于它是硬编码的,而是它的"魔力":你没有理由选择0.000001超过0.000005或0.0000000000001,是吗?请注意,它float可以近似代表后者,但仍然是较小的值 - 第一个非零数字,它只是精度的大约7位小数!

如果您要使用固定的epsilon,您应该根据您使用它的特定代码段的要求选择它.另一种方法是使用相对误差范围(有关详细信息,请参见顶部的链接),或者更好,或将浮点数作为整数进行比较.

  • 值得一提的是,Bruce Dawson已经提到他关于比较浮点数的文章现在已经过时了,读者应该参考[2012版](http://randomascii.wordpress.com/2012/02/25/而是compare- floating-point-numbers-2012-edition /). (2认同)

Pup*_*ppy 12

标准提供epsilon值.它在<limits>,您可以通过std::numeric_limits<float>::epsilon和访问该值std::numeric_limits<double>::epsilon.还有其他值,但我没有检查到底是什么.

  • 虽然要小心'epsilon`不是提问者使用的常数容差的直接替代.它表示值1.0的最低有效位为1,因此如果您的值大约为2,则它太小而无法提供任何容差.有效使用起来相当困难. (6认同)

Dan*_*ügt 9

您可以使用一个值(或最小epsilon的因子)std::nextafter测试两个double最小的epsilon.

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

bool nearly_equal(double a, double b, int factor /* a factor of epsilon */)
{
  double min_a = a - (a - std::nextafter(a, std::numeric_limits<double>::lowest())) * factor;
  double max_a = a + (std::nextafter(a, std::numeric_limits<double>::max()) - a) * factor;

  return min_a <= b && max_a >= b;
}
Run Code Online (Sandbox Code Playgroud)


Jel*_*pen 8

这是 @geotavros 解决方案的 C++11 实现。它利用了新std::numeric_limits<T>::epsilon()函数,并且std::fabs()现在std::fmax()float,double和的重载long float

template<typename T>
static bool AreEqual(T f1, T f2) { 
  return (std::fabs(f1 - f2) <= std::numeric_limits<T>::epsilon() * std::fmax(std::fabs(f1), std::fabs(f2)));
}
Run Code Online (Sandbox Code Playgroud)


ddy*_*yer 7

您应该知道,如果您要比较两个浮点数是否相等,那么您本质上就是做错了.在比较中添加一个slop因子是不够的.

  • 单元测试怎么样?如果我正在测试一个算法并且我想检查给定输入值的结果是否接近预期(浮点)值? (3认同)

Dmi*_*riy 5

谢谢你的回答,他们帮助了我很多.我读过这些材料:第一第二

答案是使用我自己的函数进行相对比较:

bool areEqualRel(float a, float b, float epsilon) {
    return (fabs(a - b) <= epsilon * std::max(fabs(a), fabs(b)));
}
Run Code Online (Sandbox Code Playgroud)

这是满足我需求的最合适的解决方案.但是我写了一些测试和其他比较方法.我希望这对某些人有用.areEqualRel通过了这些测试,其他则没有.

#include <iostream>
#include <limits>
#include <algorithm>

using std::cout;
using std::max;

bool areEqualAbs(float a, float b, float epsilon) {
    return (fabs(a - b) <= epsilon);
}

bool areEqual(float a, float b, float epsilon) {
    return (fabs(a - b) <= epsilon * std::max(1.0f, std::max(a, b)));
}

bool areEqualRel(float a, float b, float epsilon) {
    return (fabs(a - b) <= epsilon * std::max(fabs(a), fabs(b)));
}

int main(int argc, char *argv[])
{
    cout << "minimum: " << FLT_MIN      << "\n";
    cout << "maximum: " << FLT_MAX      << "\n";
    cout << "epsilon: " << FLT_EPSILON  << "\n";

    float a = 0.0000001f;
    float b = 0.0000002f;
    if (areEqualRel(a, b, FLT_EPSILON)) {
        cout << "are equal a: " << a << " b: " << b << "\n";
    }
    a = 1000001.f;
    b = 1000002.f;
    if (areEqualRel(a, b, FLT_EPSILON)) {
        cout << "are equal a: " << a << " b: " << b << "\n";
    }
}
Run Code Online (Sandbox Code Playgroud)