如何正确和标准地比较花车?

每当我开始一个新的项目,当我需要比较一些浮点或双variables,我写这样的代码:

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

然后我想摆脱这些魔术variables0.000001(和0.00000000001为双)和晶圆厂,所以我写了一个内联函数和一些定义:

 #define FLOAT_TOL 0.000001 

所以我想知道有没有这样做的标准方法? 可能是一些标准的头文件? 有浮动和双重限制(最小值和最大值)

从浮点指南 :

这是一个不好的方法,因为一个固定的epsilonselect,因为它“看起来很小”实际上可能是太大,当被比较的数字是非常小的。 比较将会返回“真实”的数字是完全不同的。 而当数字非常大时,ε可能会小于最小舍入误差,因此比较总是返回“false”。

这里的“魔术数字”的问题并不是硬编码,而是“魔术”:你没有理由select0.000001超过0.000005或0.0000000000001,是吗? 请注意, float可以近似表示后者,也可以是更小的值 – 第一个非零数字之后 ,精度只有7个小数点左右!

如果你打算使用一个固定的epsilon,你应该根据你使用它的特定代码的要求真正地select它。 另一种方法是使用相对误差裕度(详见顶部的链接),或者甚至更好,或者将浮点数比较为整数 。

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

你应该知道,如果你比较两个花车的平等,你本质上做错了事情。 在比较中增加一个斜率因子是不够的。

您可以使用std::nextafter来testing两个具有最小epsilon值(或最小epsilon因子)的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; } 

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

[编辑:使它只是一点点可读性。]

但是另外,这取决于你在做什么。

谢谢你的回答,他们帮了我很多。 我读过这些资料: 第一和第二

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

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

这是我最需要的解决scheme。 不过,我写了一些testing和其他比较方法。 我希望这对别人有用。 areEqualRel通过这些testing,而其他则不。

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

这里是@geotavros解决scheme的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(fabs(f1), fabs(f2))); } 

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

摘抄:

  • 如果你比较零,那么基于相对ε和ULP的比较通常是没有意义的。 你需要使用一个绝对的epsilon,它的值可能是FLT_EPSILON的一小部分以及你的计算的input。 也许。
  • 如果你是比较一个非零的数字,那么相对ε或ULP的比较可能是你想要的。 你可能会需要一些FLT_EPSILON的倍数作为你的相对epsilon,或者是less量的ULP。 如果你确切地知道你在比较什么数字,可以使用绝对epsilon。