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

时间:2010-12-28 17:38:13

标签: 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;
}

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

#define FLOAT_TOL 0.000001

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

也很好

8 个答案:

答案 0 :(得分:16)

来自The Floating-Point Guide

  

这是一种不好的方法,因为a   选择固定epsilon,因为它“看起来   小“实际上可能太大了   当被比较的数字是   非常小。比较   对于那些数字会返回“true”   是完全不同的。而当   数字非常大,epsilon   可能最终小于   最小的舍入误差,这样的   比较总是返回“假”。

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

如果您要使用固定的epsilon,您应该根据您使用它的特定代码段的要求选择它。另一种方法是使用相对误差范围(有关详细信息,请参阅顶部的链接),或者更好,或compare the floats as integers

答案 1 :(得分:12)

标准提供epsilon值。它位于<limits>,您可以按std::numeric_limits<float>::epsilonstd::numeric_limits<double>::epsilon访问该值。还有其他值,但我没有检查到底是什么。

答案 2 :(得分:7)

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

答案 3 :(得分:7)

您可以使用std::nextafter来测试值{(或最小epsilon因子)上最小epsil的两个double

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;
}

答案 4 :(得分:5)

感谢您的回答,他们给了我很多帮助。我已阅读这些材料:firstsecond

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

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

这是满足我需求的最合适的解决方案。但是我写了一些测试和其他比较方法。我希望这对某些人有用。 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";
    }
}

答案 5 :(得分:4)

您应该使用float.h中的标准定义:

#define DBL_EPSILON     2.2204460492503131e-016 /* smallest float value such that 1.0+DBL_EPSILON != 1.0 */

或numeric_limits类:

// excerpt
template<>
class numeric_limits<float> : public _Num_float_base
{
public:
    typedef float T;

    // return minimum value
    static T (min)() throw();

    // return smallest effective increment from 1.0
    static T epsilon() throw();

    // return largest rounding error
    static T round_error() throw();

    // return minimum denormalized value
     static T denorm_min() throw();
};

[编辑:让它更具可读性。]

但另外,这取决于你所追求的目标。

答案 6 :(得分:3)

这是@geotavros解决方案的c ++ 11实现。它利用了新的std::numeric_limits<T>::epsilon()函数以及std::fabs()std::fmax()现在对floatdoublelong 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)));
}

答案 7 :(得分:1)

这篇文章全面解释了如何比较浮点数: http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/

摘录:

  
      
  • 如果您要与零进行比较,那么相对epsilons和基于ULP的比较通常是没有意义的。你需要使用一个   绝对的epsilon,其值可能是一些小的倍数   FLT_EPSILON和计算的输入。也许。
  •   
  • 如果您要与非零数字进行比较,那么基于相对epsilons或ULP的比较可能就是您想要的。你会   可能想要一些FLT_EPSILON的小倍数给你的亲戚   epsilon,或一些少数ULP。绝对的epsilon可能是   如果您确切地知道您正在比较的数字,请使用。
  •