C++ 浮点相等性和容差

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17404513/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:10:13  来源:igfitidea点击:

Floating point equality and tolerances

c++cfloating-pointfloating-accuracy

提问by doron

Comparing two floating point number by something like a_float == b_floatis looking for trouble since a_float / 3.0 * 3.0might not be equal to a_floatdue to round off error.

通过类似的a_float == b_float方式比较两个浮点数正在寻找麻烦,因为由于舍入错误a_float / 3.0 * 3.0可能不等于a_float

What one normally does is something like fabs(a_float - b_float) < tol.

人们通常做的是类似的事情fabs(a_float - b_float) < tol

How does one calculate tol?

怎么计算tol

Ideally tolerance should be just larger than the value of one or two of the least significant figures. So if the single precision floating point number is use tol = 10E-6should be about right. However this does not work well for the general case where a_floatmight be very small or might be very large.

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

How does one calculate tolcorrectly for all general cases? I am interested in C or C++ cases specifically.

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

回答by aryan

This blogpost contains an example, fairly foolproof implementation, and detailed theory behind it http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/it is also one of a series, so you can always read more. In short: use ULP for most numbers, use epsilon for numbers near zero, but there are still caveats. If you want to be sure about your floating point math i recommend reading whole series.

这篇博文包含一个例子,相当万无一失的实现,以及它背后的详细理论 http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/它也是一个系列之一,因此您可以随时阅读更多内容。简而言之:对大多数数字使用 ULP,对接近零的数字使用 epsilon,但仍有一些注意事项。如果你想确定你的浮点数学,我建议你阅读整个系列。

回答by unwind

As far as I know, one doesn't.

据我所知,一个没有。

There is no general "right answer", since it can depend on the application's requirement for precision.

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

For instance, a 2D physics simulation working in screen-pixels might decide that 1/4 of a pixel is good enough, while a 3D CAD system used to design nuclear plant internals might not.

例如,在屏幕像素中工作的 2D 物理模拟可能会认为 1/4 像素就足够了,而用于设计核电站内部结构的 3D CAD 系统可能不会。

I can't see a way to programmatically decide this from the outside.

我看不到从外部以编程方式决定这一点的方法。

回答by Chris Dodd

The C header file <float.h>gives you the constants FLT_EPSILONand DBL_EPSILON, which is the difference between 1.0 and the smallest number larger than 1.0 that a float/double can represent. You can scale that by the size of your numbers and the rounding error you wish to tolerate:

C 头文件<float.h>为您提供常量FLT_EPSILONand DBL_EPSILON,它是 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);
}

回答by chux - Reinstate Monica

Welcome to the world of traps, snares and loopholes. As mentioned elsewhere, a general purpose solution for floating point equality and tolerances does notexist. Given that, there are tools and axioms that a programmer may use in select cases.

欢迎来到充满陷阱、陷阱和漏洞的世界。如别处所述,存在用于浮点相等性和容差的通用解决方案。鉴于此,程序员可以在特定情况下使用一些工具和公理。

fabs(a_float - b_float) < tolhas the shortcoming OP mentioned: "does not work well for the general case where a_float might be very small or might be very large." fabs(a_float - ref_float) <= fabs(ref_float * tol)copes with the variant ranges much better.

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

OP's "single precision floating point number is use tol = 10E-6" is a bit worrisome for C and C++ so easily promote floatarithmetic to doubleand then it's the "tolerance" of double, not float, that comes into play. Consider float f = 1.0; printf("%.20f\n", f/7.0);So many new programmers do not realize that the 7.0caused a doubleprecision calculation. Recommend using doublethough out your code except where large amounts of data need the floatsmaller size.

OP 的“单精度浮点数使用 tol = 10E-6”对于 C 和 C++ 来说有点令人担忧,因此很容易将float算术提升到double,然后是 的“容差” double,而不是float,发挥作用。考虑float f = 1.0; printf("%.20f\n", f/7.0);很多新程序员没有意识到这7.0导致了double精确计算。建议使用double你的代码,除非大量数据需要float较小的大小。

C99 provides nextafter()which can be useful in helping to gauge "tolerance". Using it, one can determine the next representable number. This will help with the OP "... the full number of significant digits for the storage type minus one ... to allow for roundoff error." if ((nextafter(x, -INF) <= y && (y <= nextafter(x, +INF))) ...

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

The kindof tolor "tolerance" used is often the crux of the matter. Most often (IMHO) a relativetolerance is important. e. g. "Are x and y within 0.0001%"? Sometimes an absolutetolerance is needed. e.g. "Are x and y within 0.0001"?

样的tol或“宽容”使用往往是问题的关键。大多数情况下(恕我直言)相对容差很重要。例如“x 和 y 是否在 0.0001% 以内”?有时需要绝对的公差。例如“x 和 y 是否在 0.0001 之内”?

The valueof the tolerance is often debatable for the best value is often situation dependent. Comparing within 0.01 may work for a financial application for Dollars but not Yen. (Hint: be sure to use a coding style that allows easy updates.)

容差的通常是有争议的,因为最佳值通常取决于情况。0.01 以内的比较可能适用于美元的金融应用程序,但不适用于日元。(提示:确保使用易于更新的编码风格。)

回答by Daniel Laügt

Rounding error varies according to values used for operations.

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

Instead of a fixed tolerance, you can probably use a factor of epsilon like:

除了固定容差之外,您还可以使用 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;
}

回答by MartaF

Although the value of the tolerance depends on the situation, if you are looking for precision comparasion you could used as tolerance the machine epsilon value, numeric_limits::epsilon() (Library limits). The function returns the difference between 1 and the smallest value greater than 1 that is representable for the data type. http://msdn.microsoft.com/en-us/library/6x7575x3.aspx

尽管容差的值取决于情况,但如果您正在寻找精度比较,您可以将机器 epsilon 值 numeric_limits::epsilon() (库限制)用作容差。该函数返回 1 与该数据类型可表示的大于 1 的最小值之间的差值。 http://msdn.microsoft.com/en-us/library/6x7575x3.aspx

The value of epsilon differs if you are comparing floats or doubles. For instance, in my computer, if comparing floats the value of epsilon is 1.1920929e-007 and if comparing doubles the value of epsilon is 2.2204460492503131e-016.

如果您比较浮点数或双精度数,epsilon 的值会有所不同。例如,在我的计算机中,如果比较浮点数,epsilon 的值为 1.1920929e-007,如果比较双精度数,epsilon 的值为 2.2204460492503131e-016。

For a relative comparison between x and y, multiply the epsilon by the maximum absolute value of x and y.

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

The result above could be multiplied by the ulps (units in the last place) which allows you to play with the precision.

上面的结果可以乘以 ulps(最后一个单位),这使您可以精确地进行游戏。

#include <iostream>
#include <cmath>
#include <limits>

template<class T> bool are_almost_equal(T x, T y, int ulp)
{
    return std::abs(x-y) <= std::numeric_limits<T>::epsilon() * std::max(std::abs(x), std::abs(y)) * ulp
}

回答by brian beuning

When I need to compare floats, I use code like this

当我需要比较浮点数时,我使用这样的代码

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