36

每次我开始一个新项目并且需要比较一些浮点或双精度变量时,我都会编写如下代码:

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

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

4

8 回答 8

21

来自浮点指南

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

这里“幻数”的问题不在于它是硬编码的,而在于它是“幻数”:你真的没有理由选择 0.000001 而不是 0.000005 或 0.0000000000001,对吗?请注意,它可以近似地表示后者和更小的值——在第一个非零数字之后float,它只是大约 7 位小数的精度!

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

于 2010-12-28T19:50:09.293 回答
14

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

于 2010-12-28T18:16:45.960 回答
14

您可以std::nextafter用于测试两个double具有最小 epsilon 的值(或最小 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;
}
于 2016-02-07T11:34:47.547 回答
8

感谢您的回答,他们对我帮助很大。我已经阅读了这些材料:第一第二

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

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";
    }
}
于 2011-01-04T21:56:03.617 回答
4

您应该知道,如果您比较两个浮点数是否相等,那么您本质上是在做错事。在比较中添加一个倾斜因素是不够的。

于 2010-12-28T17:44:06.997 回答
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();
};

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

但除此之外,这取决于你所追求的。

于 2010-12-28T18:30:54.303 回答
4

这是@geotavros 解决方案的c++11 实现。它利用了新函数以及and现在有,和的重载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)));
}
于 2016-10-14T09:11:40.050 回答
1

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

摘抄:

  • 如果您要与零进行比较,那么基于相对 epsilon 和 ULP 的比较通常是没有意义的。您需要使用绝对 epsilon,其值可能是 FLT_EPSILON 的一些小倍数以及计算的输入。也许。
  • 如果您要与非零数进行比较,那么基于相对 epsilon 或 ULP 的比较可能是您想要的。您可能需要一些小的 FLT_EPSILON 倍数作为您的相对 epsilon,或者一些少量的 ULP。如果您确切知道要比较的数字,则可以使用绝对 epsilon。
于 2013-07-19T05:00:21.923 回答