浮点平等和容差

比较两个浮点数,如a_float == b_float正在寻找麻烦,因为由于四舍五入错误, a_float / 3.0 * 3.0可能不等于a_float

通常情况下, fabs(a_float - b_float) < tol

如何计算tol

理想情况下,容忍度应该大于一个或两个最低有效数字的值。 所以如果单精度浮点数使用tol = 10E-6应该是正确的。 但是,对于a_float可能非常小或可能非常大的一般情况,这不起作用。

如何正确计算所有一般情况下的tol ? 我特别感兴趣的是C或C ++的情况。

这个博客文章包含一个例子,相当简单的实现和详细的理论背后http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/它也是一个系列之一,所以你总是可以阅读更多。; 简而言之:对于大多数数字使用ULP,对接近零的数字使用epsilon,但是仍有一些注意事项。 如果你想确定你的浮点math,我build议阅读整个系列。

据我所知,一个不是。

没有一般的“正确答案”,因为它可以取决于应用程序对精度的要求。

例如,在屏幕像素中工作的2D物理模拟可能决定1/4像素是足够好的,而用于devise核电站内部的3D CAD系统可能不会。

我看不到从外部以编程方式决定这种方式。

C头文件<float.h>为你提供了常量FLT_EPSILONDBL_EPSILON ,它们是1.0和1.0之间的差值,浮点/双精度可以表示大于1.0的值。 您可以按照您希望容忍的数字大小和舍入误差进行缩放:

 #include <float.h> #ifndef DBL_TRUE_MIN /* DBL_TRUE_MIN is a common non-standard extension for the minimum denorm value * DBL_MIN is the minimum non-denorm value -- use that if TRUE_MIN is not defined */ #define DBL_TRUE_MIN DBL_MIN #endif /* return the difference between |x| and the next larger representable double */ double dbl_epsilon(double x) { int exp; if (frexp(x, &exp) == 0.0) return DBL_TRUE_MIN; return ldexp(DBL_EPSILON, exp-1); } 

欢迎来到陷阱,网罗和漏洞的世界。 如其他地方所提到的,浮点相等和容差的通用解决scheme存在。 鉴于此,程序员可以在特定情况下使用工具和公理。

fabs(a_float - b_float) < tol具有OP的缺点:“对于a_float可能非常小或可能非常大的一般情况,效果不佳。 fabs(a_float - ref_float) <= fabs(ref_float * tol)更好地应对变种范围。

OP的“单精度浮点数使用tol = 10E-6”对于C和C ++来说有点令人担忧,所以很容易将float运算提高double ,然后是double float运算的“容差”。 考虑float f = 1.0; printf("%.20f\n", f/7.0); float f = 1.0; printf("%.20f\n", f/7.0); 许多新程序员没有意识到7.0引起了double精度计算。 推荐使用double代替你的代码,除非大量数据需要float更小的尺寸。

C99提供了nextafter() ,它可以帮助衡量“宽容”。 使用它,可以确定下一个可表示的数字。 这将有助于OP“…存储types的全部有效位数减1 …以允许舍入误差”。 if ((nextafter(x, -INF) <= y && (y <= nextafter(x, +INF))) ...

所使用的那种 “宽容”往往是问题的关键。 (恕我直言), 相对容忍是很重要的。 例如“x和y在0.0001%以内”? 有时需要一个绝对的宽容。 例如“x和y在0.0001以内”?

宽容的价值往往是值得商榷的,因为最好的价值往往是情境依赖的。 在0.01以内的比较可能适用于美元而不是日元的金融申请。 (提示:一定要使用容易更新的编码风格。)

舍入误差根据用于操作的值而变化。

而不是一个固定的宽容,你可以使用epsilon的因子,如:

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

虽然宽容的价值取决于情况,但如果您正在寻找精确的对比度,您可以将其作为容差值,即机器epsilon值numeric_limits :: epsilon()(库限制)。 该函数返回1和表示数据types的最大值之间的差值。 http://msdn.microsoft.com/en-us/library/6x7575x3.aspx

如果您比较花车或双打,epsilon的价值会有所不同。 例如,在我的电脑里,如果比较浮点数,epsilon的值是1.1920929e-007,比较double的值是2.2204460492503131e-016。

对于x和y的相对比较,将ε乘以x和y的最大绝对值。

上面的结果可以乘以ulps(最后一个单位),让你玩的精度。

 #include <iostream> #include <cmath> #include <limits> template<class T> bool are_almost_equal(T x, T y, int ulp) { if( std::abs(xy) <= std::numeric_limits<T>::epsilon() * std::max(std::abs(x), std::abs(y)) * ulp ){ return true; }else{ return false; } } 

当我需要比较浮动,我使用这样的代码

 bool same( double a, double b, double error ) { double x; if( a == 0 ) { x = b; } else if( b == 0 ) { x = a; } else { x = (ab) / a; } return fabs(x) < error; }