浮动和双重比较最有效的方法是什么?

时间:2008-08-20 02:09:34

标签: c++ algorithm optimization floating-point

比较两个double或两个float值的最有效方法是什么?

简单地这样做是不正确的:

bool CompareDoubles1 (double A, double B)
{
   return A == B;
}

但是像:

bool CompareDoubles2 (double A, double B) 
{
   diff = A - B;
   return (diff < EPSILON) && (-diff < EPSILON);
}

似乎浪费处理。

有谁知道更聪明的浮动比较器?

32 个答案:

答案 0 :(得分:417)

使用任何其他建议时要格外小心。这一切都取决于背景。

我花了很长时间跟踪假设a==b if |a-b|<epsilon的系统中的错误。潜在的问题是:

  1. 算法中的隐含假设,即a==bb==c然后a==c

  2. 对以英寸为单位测量的线使用相同的epsilon,以密耳(.001英寸)为单位测量线。那是a==b1000a!=1000b。 (这就是AlmostEqual2sComplement要求epsilon或最大ULPS的原因。)

  3. 对角度余弦和线条长度使用相同的epsilon!

  4. 使用此类比较功能对集合中的项目进行排序。 (在这种情况下,使用内置C ++运算符== for double产生了正确的结果。)

  5. 就像我说的:这完全取决于上下文以及ab的预期大小。

    BTW,std::numeric_limits<double>::epsilon()是“机器epsilon”。它是1.0和下一个值之间的差值,可用双精度表示。我猜它可以在比较函数中使用,但只有在预期值小于1时才会使用。(这是对@ cdv答案的回应......)

    另外,如果你在int中基本上有doubles算术(这里我们使用双精度来保存某些情况下的int值)你的算术是正确的。例如,4.0 / 2.0将与1.0 + 1.0相同。只要您不执行导致分数(4.0 / 3.0)或不超出int大小的事情。

答案 1 :(得分:171)

与epsilon值的比较是大多数人所做的(即使在游戏编程中)。

你应该稍微改变你的实现:

bool AreSame(double a, double b)
{
    return fabs(a - b) < EPSILON;
}

编辑:Christer在recent blog post上添加了关于此主题的一堆精彩信息。享受。

答案 2 :(得分:111)

我发现Google C++ Testing Framework包含一个很好的跨平台模板的AlmostEqual2sComplement实现,它适用于双精度和浮点数。鉴于它是根据BSD许可证发布的,只要您保留许可证,在您自己的代码中使用它应该没有问题。我从 http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.h https://github.com/google/googletest/blob/master/googletest/include/gtest/internal/gtest-internal.h中提取了以下代码,并将许可证添加到最顶层。

确保#define GTEST_OS_WINDOWS为某个值(或者将代码更改为适合您代码库的代码 - 毕竟它是BSD许可的。)

用法示例:

double left  = // something
double right = // something
const FloatingPoint<double> lhs(left), rhs(right);

if (lhs.AlmostEquals(rhs)) {
  //they're equal!
}

以下是代码:

// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
//
// The Google C++ Testing Framework (Google Test)


// This template class serves as a compile-time function from size to
// type.  It maps a size in bytes to a primitive type with that
// size. e.g.
//
//   TypeWithSize<4>::UInt
//
// is typedef-ed to be unsigned int (unsigned integer made up of 4
// bytes).
//
// Such functionality should belong to STL, but I cannot find it
// there.
//
// Google Test uses this class in the implementation of floating-point
// comparison.
//
// For now it only handles UInt (unsigned int) as that's all Google Test
// needs.  Other types can be easily added in the future if need
// arises.
template <size_t size>
class TypeWithSize {
 public:
  // This prevents the user from using TypeWithSize<N> with incorrect
  // values of N.
  typedef void UInt;
};

// The specialization for size 4.
template <>
class TypeWithSize<4> {
 public:
  // unsigned int has size 4 in both gcc and MSVC.
  //
  // As base/basictypes.h doesn't compile on Windows, we cannot use
  // uint32, uint64, and etc here.
  typedef int Int;
  typedef unsigned int UInt;
};

// The specialization for size 8.
template <>
class TypeWithSize<8> {
 public:
#if GTEST_OS_WINDOWS
  typedef __int64 Int;
  typedef unsigned __int64 UInt;
#else
  typedef long long Int;  // NOLINT
  typedef unsigned long long UInt;  // NOLINT
#endif  // GTEST_OS_WINDOWS
};


// This template class represents an IEEE floating-point number
// (either single-precision or double-precision, depending on the
// template parameters).
//
// The purpose of this class is to do more sophisticated number
// comparison.  (Due to round-off error, etc, it's very unlikely that
// two floating-points will be equal exactly.  Hence a naive
// comparison by the == operation often doesn't work.)
//
// Format of IEEE floating-point:
//
//   The most-significant bit being the leftmost, an IEEE
//   floating-point looks like
//
//     sign_bit exponent_bits fraction_bits
//
//   Here, sign_bit is a single bit that designates the sign of the
//   number.
//
//   For float, there are 8 exponent bits and 23 fraction bits.
//
//   For double, there are 11 exponent bits and 52 fraction bits.
//
//   More details can be found at
//   http://en.wikipedia.org/wiki/IEEE_floating-point_standard.
//
// Template parameter:
//
//   RawType: the raw floating-point type (either float or double)
template <typename RawType>
class FloatingPoint {
 public:
  // Defines the unsigned integer type that has the same size as the
  // floating point number.
  typedef typename TypeWithSize<sizeof(RawType)>::UInt Bits;

  // Constants.

  // # of bits in a number.
  static const size_t kBitCount = 8*sizeof(RawType);

  // # of fraction bits in a number.
  static const size_t kFractionBitCount =
    std::numeric_limits<RawType>::digits - 1;

  // # of exponent bits in a number.
  static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount;

  // The mask for the sign bit.
  static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1);

  // The mask for the fraction bits.
  static const Bits kFractionBitMask =
    ~static_cast<Bits>(0) >> (kExponentBitCount + 1);

  // The mask for the exponent bits.
  static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask);

  // How many ULP's (Units in the Last Place) we want to tolerate when
  // comparing two numbers.  The larger the value, the more error we
  // allow.  A 0 value means that two numbers must be exactly the same
  // to be considered equal.
  //
  // The maximum error of a single floating-point operation is 0.5
  // units in the last place.  On Intel CPU's, all floating-point
  // calculations are done with 80-bit precision, while double has 64
  // bits.  Therefore, 4 should be enough for ordinary use.
  //
  // See the following article for more details on ULP:
  // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm.
  static const size_t kMaxUlps = 4;

  // Constructs a FloatingPoint from a raw floating-point number.
  //
  // On an Intel CPU, passing a non-normalized NAN (Not a Number)
  // around may change its bits, although the new value is guaranteed
  // to be also a NAN.  Therefore, don't expect this constructor to
  // preserve the bits in x when x is a NAN.
  explicit FloatingPoint(const RawType& x) { u_.value_ = x; }

  // Static methods

  // Reinterprets a bit pattern as a floating-point number.
  //
  // This function is needed to test the AlmostEquals() method.
  static RawType ReinterpretBits(const Bits bits) {
    FloatingPoint fp(0);
    fp.u_.bits_ = bits;
    return fp.u_.value_;
  }

  // Returns the floating-point number that represent positive infinity.
  static RawType Infinity() {
    return ReinterpretBits(kExponentBitMask);
  }

  // Non-static methods

  // Returns the bits that represents this number.
  const Bits &bits() const { return u_.bits_; }

  // Returns the exponent bits of this number.
  Bits exponent_bits() const { return kExponentBitMask & u_.bits_; }

  // Returns the fraction bits of this number.
  Bits fraction_bits() const { return kFractionBitMask & u_.bits_; }

  // Returns the sign bit of this number.
  Bits sign_bit() const { return kSignBitMask & u_.bits_; }

  // Returns true iff this is NAN (not a number).
  bool is_nan() const {
    // It's a NAN if the exponent bits are all ones and the fraction
    // bits are not entirely zeros.
    return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
  }

  // Returns true iff this number is at most kMaxUlps ULP's away from
  // rhs.  In particular, this function:
  //
  //   - returns false if either number is (or both are) NAN.
  //   - treats really large numbers as almost equal to infinity.
  //   - thinks +0.0 and -0.0 are 0 DLP's apart.
  bool AlmostEquals(const FloatingPoint& rhs) const {
    // The IEEE standard says that any comparison operation involving
    // a NAN must return false.
    if (is_nan() || rhs.is_nan()) return false;

    return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_)
        <= kMaxUlps;
  }

 private:
  // The data type used to store the actual floating-point number.
  union FloatingPointUnion {
    RawType value_;  // The raw floating-point number.
    Bits bits_;      // The bits that represent the number.
  };

  // Converts an integer from the sign-and-magnitude representation to
  // the biased representation.  More precisely, let N be 2 to the
  // power of (kBitCount - 1), an integer x is represented by the
  // unsigned number x + N.
  //
  // For instance,
  //
  //   -N + 1 (the most negative number representable using
  //          sign-and-magnitude) is represented by 1;
  //   0      is represented by N; and
  //   N - 1  (the biggest number representable using
  //          sign-and-magnitude) is represented by 2N - 1.
  //
  // Read http://en.wikipedia.org/wiki/Signed_number_representations
  // for more details on signed number representations.
  static Bits SignAndMagnitudeToBiased(const Bits &sam) {
    if (kSignBitMask & sam) {
      // sam represents a negative number.
      return ~sam + 1;
    } else {
      // sam represents a positive number.
      return kSignBitMask | sam;
    }
  }

  // Given two numbers in the sign-and-magnitude representation,
  // returns the distance between them as an unsigned number.
  static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1,
                                                     const Bits &sam2) {
    const Bits biased1 = SignAndMagnitudeToBiased(sam1);
    const Bits biased2 = SignAndMagnitudeToBiased(sam2);
    return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
  }

  FloatingPointUnion u_;
};
编辑:这篇文章是4岁。它可能仍然有效,代码很好,但有些人发现了改进。最好直接从Google Test源代码获取最新版本的AlmostEquals,而不是我在此处粘贴的那个。

答案 3 :(得分:92)

比较浮点数取决于上下文。因为即使改变操作顺序也会产生不同的结果,重要的是要知道你想要数字的“相等”。

在查看浮点比较时,布鲁斯道森的

Comparing floating point numbers是一个很好的起点。

以下定义来自The art of computer programming by Knuth

bool approximatelyEqual(float a, float b, float epsilon)
{
    return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool essentiallyEqual(float a, float b, float epsilon)
{
    return fabs(a - b) <= ( (fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool definitelyGreaterThan(float a, float b, float epsilon)
{
    return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool definitelyLessThan(float a, float b, float epsilon)
{
    return (b - a) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

当然,选择epsilon取决于上下文,并确定您希望数字的相等程度。

比较浮点数的另一种方法是查看数字的ULP(最后位置的单位)。虽然没有专门处理比较,但文章What every computer scientist should know about floating point numbers是理解浮点如何工作以及陷阱是什么的良好资源,包括ULP是什么。

答案 4 :(得分:46)

有关更深入的方法,请阅读Comparing floating point numbers。以下是该链接的代码段:

// Usable AlmostEqual function    
bool AlmostEqual2sComplement(float A, float B, int maxUlps)    
{    
    // Make sure maxUlps is non-negative and small enough that the    
    // default NAN won't compare as equal to anything.    
    assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024);    
    int aInt = *(int*)&A;    
    // Make aInt lexicographically ordered as a twos-complement int    
    if (aInt < 0)    
        aInt = 0x80000000 - aInt;    
    // Make bInt lexicographically ordered as a twos-complement int    
    int bInt = *(int*)&B;    
    if (bInt < 0)    
        bInt = 0x80000000 - bInt;    
    int intDiff = abs(aInt - bInt);    
    if (intDiff <= maxUlps)    
        return true;    
    return false;    
}

答案 5 :(得分:27)

在C ++中获取epsilon的便携式方法是

#include <limits>
std::numeric_limits<double>::epsilon()

然后比较函数变为

#include <cmath>
#include <limits>

bool AreSame(double a, double b) {
    return std::fabs(a - b) < std::numeric_limits<double>::epsilon();
}

答案 6 :(得分:24)

意识到这是一个老线程,但是这篇文章是我在比较浮点数时发现的最直接的文章之一,如果你想探索更多,它也有更详细的参考,主要网站包含一个完整的处理浮点数The Floating-Point Guide :Comparison的问题范围。

我们可以在Floating-point tolerances revisited中找到更实用的文章,并注意到绝对容差测试,在C ++中归结为此:

bool absoluteToleranceCompare(double x, double y)
{
    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon() ;
}

相对宽容测试:

bool relativeToleranceCompare(double x, double y)
{
    double maxXY = std::max( std::fabs(x) , std::fabs(y) ) ;
    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon()*maxXY ;
}

文章指出绝对测试在xy较大时失败,在相对小的情况下失败。假设他的绝对和相对容差是相同的,组合测试将如下所示:

bool combinedToleranceCompare(double x, double y)
{
    double maxXYOne = std::max( { 1.0, std::fabs(x) , std::fabs(y) } ) ;

    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon()*maxXYOne ;
}

答案 7 :(得分:14)

您编写的代码有问题:

return (diff < EPSILON) && (-diff > EPSILON);

正确的代码是:

return (diff < EPSILON) && (diff > -EPSILON);

(...而且这是不同的)

我想知道在某些情况下,晶圆厂不会让你失去懒惰的评价。我会说这取决于编译器。你可能想尝试两者。如果它们的平均值相等,那就采用fabs实现。

如果您有关于两个浮动中哪一个更可能比其他浮动更大的信息,您可以按比较顺序播放以更好地利用延迟评估。

最后,通过内联此函数可能会获得更好的结果。虽然不太可能改善......

编辑:OJ,感谢您更正代码。我相应地删除了我的评论

答案 8 :(得分:14)

  

`返回fabs(a - b)&lt; EPSILON;

如果符合以下条件,则可以。

  • 输入的数量级没有太大变化
  • 非常少量的相反符号可视为相等

但否则它会让你陷入困境。双精度数字的分辨率约为16位小数。如果您比较的两个数字的幅度大于EPSILON * 1.0E16,那么您可能会说:

return a==b;

我将研究一种不同的方法,假设您需要担心第一个问题,并假设第二个问题对您的应用程序很好。解决方案就像:

#define VERYSMALL  (1.0E-150)
#define EPSILON    (1.0E-8)
bool AreSame(double a, double b)
{
    double absDiff = fabs(a - b);
    if (absDiff < VERYSMALL)
    {
        return true;
    }

    double maxAbs  = max(fabs(a) - fabs(b));
    return (absDiff/maxAbs) < EPSILON;
}

这在计算上是昂贵的,但它有时是所要求的。这是我们在公司必须做的事情,因为我们处理的是工程库,输入可能会有几十个数量级。

无论如何,重点在于(并且几乎适用于所有编程问题):评估您的需求,然后提出解决方案来满足您的需求 - 不要认为简单的答案将满足您的需求。如果在您的评估之后您发现fabs(a-b) < EPSILON就足够了,那就完美了 - 使用它!但要注意它的缺点和其他可能的解决方案。

答案 9 :(得分:8)

我最后花了很长时间在这个伟大的主题中浏览材料。我怀疑每个人都想花这么多时间,所以我要强调我学到的内容和我实施的解决方案的摘要。

快速摘要

  1. 浮动比较有两个问题:你的精度和意义有限,约为零&#34;取决于背景(见下一点)。
  2. 1E-8与1E-16大致相同吗?如果您正在查看噪声传感器数据,那么可能是,但如果您正在进行分子模拟,则可能不是!结论:您始终需要在特定函数调用的上下文中考虑容差值,而不仅仅是使其成为应用程序范围内的通用硬编码常量。
  3. 对于一般的库函数,使用默认容差的参数仍然很好。典型的选择是new Date,它与float.h中的FLT_EPSILON相同。然而,这是有问题的,因为epsilon用于比较1.0之类的值,如果与epsilon不相同,则用于像1E9这样的值。 FLT_EPSILON定义为1.0。
  4. 检查数字是否在容差范围内的明显实现是numeric_limits::epsilon()但是这不起作用,因为默认epsilon定义为1.0。我们需要根据a和b来扩展或缩小epsilon。
  5. 此问题有两种解决方案:要么将epsilon设置为与fabs(a-b) <= epsilon成比例,要么您可以在a周围获得下一个可表示的数字,然后查看b是否属于该范围。前者被称为&#34;亲戚&#34;方法及以后称为ULP方法。
  6. 与0比较时,两种方法实际上都失败了。在这种情况下,应用程序必须提供正确的容差。
  7. 实用程序函数实现(C ++ 11)

    max(a,b)

答案 10 :(得分:7)

正如其他人所指出的那样,对于远离epsilon值的值,使用固定指数epsilon(例如0.0000001)将无用。例如,如果你的两个值是10000.000977和10000,那么这两个数字之间有 NO 32位浮点值 - 10000和10000.000977尽可能接近你没有位有点相同。这里,小于0.0009的ε是没有意义的;你也可以使用直线相等运算符。

同样,当两个值的大小接近epsilon时,相对误差会增加到100%。

因此,尝试将诸如0.00001的固定点数与浮点值(指数是任意的)混合是一种毫无意义的练习。只有在可以确保操作数值位于窄域(即接近某个特定指数)的情况下,以及为该特定测试正确选择epsilon值时,这才会起作用。如果你从空中拉出一个数字(“嘿!0.00001很小,那一定是好的!”),你注定会出现数字错误。我花了很多时间来调试糟糕的数字代码,其中一些可怜的schmuck抛出随机epsilon值,以使另一个测试用例工作。

如果你进行任何类型的数值编程并认为你需要达到定点epsilons,阅读布鲁斯关于比较浮点数的文章

Comparing Floating Point Numbers

答案 11 :(得分:4)

Qt实现了两个功能,也许您可​​以从中学习:

static inline bool qFuzzyCompare(double p1, double p2)
{
    return (qAbs(p1 - p2) <= 0.000000000001 * qMin(qAbs(p1), qAbs(p2)));
}

static inline bool qFuzzyCompare(float p1, float p2)
{
    return (qAbs(p1 - p2) <= 0.00001f * qMin(qAbs(p1), qAbs(p2)));
}

您可能需要以下功能,因为

  

请注意,比较p1或p2为0.0的值将不起作用,   也不能比较值之一为NaN或无穷大的值。   如果值之一始终为0.0,请改用qFuzzyIsNull。如果一个   的值可能是0.0,一种解决方案是将两者都加1.0   值。

static inline bool qFuzzyIsNull(double d)
{
    return qAbs(d) <= 0.000000000001;
}

static inline bool qFuzzyIsNull(float f)
{
    return qAbs(f) <= 0.00001f;
}

答案 12 :(得分:3)

浮点数的通用比较通常是没有意义的。如何比较真的取决于手头的问题。在许多问题中,数字被充分离散化以允许在给定容差内比较它们。不幸的是,存在同样多的问题,这种技巧并不真正起作用。举一个例子,当你的观察非常接近障碍时,考虑使用一个有问题的数字的Heaviside(步骤)函数(数字股票期权)。执行基于容差的比较不会带来太大好处,因为它会将问题从原始障碍有效地转移到两个新障碍。同样,对于这些问题没有通用的解决方案,特定的解决方案可能需要改变数值方法以实现稳定性。

答案 13 :(得分:2)

不幸的是,即使你的“浪费”代码也是错误的。 EPSILON是可以添加到 1.0 并更改其值的最小值。值 1.0 非常重要 - 添加到EPSILON时,较大的数字不会更改。现在,您可以将此值缩放为您要比较的数字,以判断它们是否不同。比较两个双打的正确表达式是:

if (fabs(a - b) <= DBL_EPSILON * fmax(fabs(a), fabs(b)))
{
    // ...
}

这是至少。但是,一般情况下,您需要考虑计算中的噪声并忽略一些最低有效位,因此更真实的比较看起来像:

if (fabs(a - b) <= 16 * DBL_EPSILON * fmax(fabs(a), fabs(b)))
{
    // ...
}

如果比较性能对您非常重要并且您知道值的范围,那么您应该使用定点数字。

答案 14 :(得分:2)

我的课程基于之前发布的答案。与谷歌的代码非常相似,但我使用偏差将所有NaN值推高到0xFF000000以上。这样可以更快地检查NaN。

此代码旨在演示该概念,而不是一般解决方案。谷歌的代码已经展示了如何计算所有平台特定值,我不想复制所有这些。我对这段代码进行了有限的测试。

typedef unsigned int   U32;
//  Float           Memory          Bias (unsigned)
//  -----           ------          ---------------
//   NaN            0xFFFFFFFF      0xFF800001
//   NaN            0xFF800001      0xFFFFFFFF
//  -Infinity       0xFF800000      0x00000000 ---
//  -3.40282e+038   0xFF7FFFFF      0x00000001    |
//  -1.40130e-045   0x80000001      0x7F7FFFFF    |
//  -0.0            0x80000000      0x7F800000    |--- Valid <= 0xFF000000.
//   0.0            0x00000000      0x7F800000    |    NaN > 0xFF000000
//   1.40130e-045   0x00000001      0x7F800001    |
//   3.40282e+038   0x7F7FFFFF      0xFEFFFFFF    |
//   Infinity       0x7F800000      0xFF000000 ---
//   NaN            0x7F800001      0xFF000001
//   NaN            0x7FFFFFFF      0xFF7FFFFF
//
//   Either value of NaN returns false.
//   -Infinity and +Infinity are not "close".
//   -0 and +0 are equal.
//
class CompareFloat{
public:
    union{
        float     m_f32;
        U32       m_u32;
    };
    static bool   CompareFloat::IsClose( float A, float B, U32 unitsDelta = 4 )
                  {
                      U32    a = CompareFloat::GetBiased( A );
                      U32    b = CompareFloat::GetBiased( B );

                      if ( (a > 0xFF000000) || (b > 0xFF000000) )
                      {
                          return( false );
                      }
                      return( (static_cast<U32>(abs( a - b ))) < unitsDelta );
                  }
    protected:
    static U32    CompareFloat::GetBiased( float f )
                  {
                      U32    r = ((CompareFloat*)&f)->m_u32;

                      if ( r & 0x80000000 )
                      {
                          return( ~r - 0x007FFFFF );
                      }
                      return( r + 0x7F800000 );
                  }
};

答案 15 :(得分:2)

这里证明使用std::numeric_limits::epsilon()并不是答案–大于1的值将失败:

以上我的评论的证据:

#include <stdio.h>
#include <limits>

double ItoD (__int64 x) {
    // Return double from 64-bit hexadecimal representation.
    return *(reinterpret_cast<double*>(&x));
}

void test (__int64 ai, __int64 bi) {
    double a = ItoD(ai), b = ItoD(bi);
    bool close = std::fabs(a-b) < std::numeric_limits<double>::epsilon();
    printf ("%.16f and %.16f %s close.\n", a, b, close ? "are " : "are not");
}

int main()
{
    test (0x3fe0000000000000L,
          0x3fe0000000000001L);

    test (0x3ff0000000000000L,
          0x3ff0000000000001L);
}

运行将产生以下输出:

0.5000000000000000 and 0.5000000000000001 are  close.
1.0000000000000000 and 1.0000000000000002 are not close.

请注意,在第二种情况下(一个且刚好大于一个),两个输入值尽可能接近,但仍比较不接近。因此,对于大于1.0的值,您最好只使用相等性测试。比较浮点值时,固定的epsilons不会节省您的时间。

答案 16 :(得分:1)

这取决于您希望比较的精确程度。如果你想比较完全相同的数字,那么就去==。 (除非你真的想要完全相同的数字,否则你几乎不想这样做。)在任何体面的平台上你也可以做到以下几点:

diff= a - b; return fabs(diff)<EPSILON;

因为fabs往往非常快。非常快,我的意思是它基本上是一个按位AND,所以最好快。

用于比较双精度和浮点数的整数技巧很不错,但往往会使各种CPU流水线难以有效处理。由于使用堆栈作为经常使用的值的临时存储区域,现在在某些有序架构中肯定不会更快。 (对于那些关心的人来说,加载点击存储。)

答案 17 :(得分:1)

https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon

上找到了另一个有趣的实现。
#include <cmath>
#include <limits>
#include <iomanip>
#include <iostream>
#include <type_traits>
#include <algorithm>



template<class T>
typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type
    almost_equal(T x, T y, int ulp)
{
    // the machine epsilon has to be scaled to the magnitude of the values used
    // and multiplied by the desired precision in ULPs (units in the last place)
    return std::fabs(x-y) <= std::numeric_limits<T>::epsilon() * std::fabs(x+y) * ulp
        // unless the result is subnormal
        || std::fabs(x-y) < std::numeric_limits<T>::min();
}

int main()
{
    double d1 = 0.2;
    double d2 = 1 / std::sqrt(5) / std::sqrt(5);
    std::cout << std::fixed << std::setprecision(20) 
        << "d1=" << d1 << "\nd2=" << d2 << '\n';

    if(d1 == d2)
        std::cout << "d1 == d2\n";
    else
        std::cout << "d1 != d2\n";

    if(almost_equal(d1, d2, 2))
        std::cout << "d1 almost equals d2\n";
    else
        std::cout << "d1 does not almost equal d2\n";
}

答案 18 :(得分:0)

您必须执行此处理以进行浮点比较,因为无法像整数类型那样完美地比较浮点数。这是各种比较运算符的功能。

浮点数等于(==

我也更喜欢减法技术,而不是依靠fabs()abs(),但是我必须在从64位PC到ATMega328微控制器(Arduino)的各种体系结构上加速它的配置,看看它对性能的影响是否很大。

所以,让我们忘记所有这些绝对值的东西,而做一些减法和比较!

修改自Microsoft's example here

/// @brief      See if two floating point numbers are approximately equal.
/// @param[in]  a        number 1
/// @param[in]  b        number 2
/// @param[in]  epsilon  A small value such that if the difference between the two numbers is
///                      smaller than this they can safely be considered to be equal.
/// @return     true if the two numbers are approximately equal, and false otherwise
bool is_float_eq(float a, float b, float epsilon) {
    return ((a - b) < epsilon) && ((b - a) < epsilon);
}
bool is_double_eq(double a, double b, double epsilon) {
    return ((a - b) < epsilon) && ((b - a) < epsilon);
}

用法示例:

constexpr float EPSILON = 0.0001; // 1e-4
is_float_eq(1.0001, 0.99998, EPSILON);

我不确定,但是在我看来,对this highly-upvoted answer下面的评论中所述的基于epsilon方法的一些批评可以通过使用可变epsilon来解决,该变量根据被比较的浮点值,如下所示:

float a = 1.0001;
float b = 0.99998;
float epsilon = std::max(std::fabs(a), std::fabs(b)) * 1e-4;

is_float_eq(a, b, epsilon);

这样,ε值随浮点值缩放,因此永远不会太小而不会变得无关紧要。

为完整起见,让我们添加其余部分:

大于(>,小于(<):

/// @brief      See if floating point number `a` is > `b`
/// @param[in]  a        number 1
/// @param[in]  b        number 2
/// @param[in]  epsilon  a small value such that if `a` is > `b` by this amount, `a` is considered
///             to be definitively > `b`
/// @return     true if `a` is definitively > `b`, and false otherwise
bool is_float_gt(float a, float b, float epsilon) {
    return a > b + epsilon;
}
bool is_double_gt(double a, double b, double epsilon) {
    return a > b + epsilon;
}

/// @brief      See if floating point number `a` is < `b`
/// @param[in]  a        number 1
/// @param[in]  b        number 2
/// @param[in]  epsilon  a small value such that if `a` is < `b` by this amount, `a` is considered
///             to be definitively < `b`
/// @return     true if `a` is definitively < `b`, and false otherwise
bool is_float_lt(float a, float b, float epsilon) {
    return a < b - epsilon;
}
bool is_double_lt(double a, double b, double epsilon) {
    return a < b - epsilon;
}

大于或等于(>=),并且小于或等于(<=

/// @brief      Returns true if `a` is definitively >= `b`, and false otherwise
bool is_float_ge(float a, float b, float epsilon) {
    return a > b - epsilon;
}
bool is_double_ge(double a, double b, double epsilon) {
    return a > b - epsilon;
}

/// @brief      Returns true if `a` is definitively <= `b`, and false otherwise
bool is_float_le(float a, float b, float epsilon) {
    return a < b + epsilon;
}
bool is_double_le(double a, double b, double epsilon) {
    return a < b + epsilon;
}

另请参见:

  1. 在我的仓库中,上面的某些功能的宏形式为utilities.h
    1. 2020年11月29日更新:这是一个进行中的工作,准备就绪时将单独回答,但我在C语言中产生了更好的,按比例缩放的epsilon版本。此文件位于utilities.c。看看。
  2. 我现在需要做的其他阅读已经完成:Floating-point tolerances revisited, by Christer Ericson

答案 19 :(得分:0)

一些有答案的实验。

#ifndef FLOATING_POINT_MATH_H
#define FLOATING_POINT_MATH_H

#ifdef _MSC_VER
#pragma once
#endif

#include <type_traits>
#include <cmath>

/** Floating point math functions with custom tolerance

@param float, double, long double.
@return bool test result.

is_approximately_equal(a, b, tolerance)
is_essentialy_equal(a, b, tolerance)
is_approximately_zero(a, tolerance)
is_definitely_greater_than(a, b, tolerance)
is_definitely_less_than(a, b, tolerance)
is_within_tolerance(a, b, tolerance)
*/

namespace
{
    template<typename T> constexpr T default_tolerance = std::numeric_limits<T>::epsilon();
    template<typename T> using Test_floating_t = typename std::enable_if<std::is_floating_point<T>::value>::type;
}



template<typename T, typename = Test_floating_t<T>> inline constexpr 
bool is_approximately_equal(const T a, const T b, const T tolerance = default_tolerance<T>) noexcept
{
    return std::fabs(a - b) <= ((std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * tolerance);
}



template<typename T, typename = Test_floating_t<T>> inline constexpr 
bool is_essentialy_equal(const T a, const T b, const T tolerance = default_tolerance<T>) noexcept
{
    return std::fabs(a - b) <= ((std::fabs(a) > std::fabs(b) ? std::fabs(b) : std::fabs(a)) * tolerance);
}



template<typename T, typename = Test_floating_t<T>> inline constexpr 
bool is_approximately_zero(const T a, const T tolerance = default_tolerance<T>) noexcept
{
    return std::fabs(a) <= tolerance;
}



template<typename T, typename = Test_floating_t<T>> inline constexpr 
bool is_definitely_greater_than(const T a, const T b, const T tolerance = default_tolerance<T>) noexcept
{
    return (a - b) > ((std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * tolerance);
}



template<typename T, typename = Test_floating_t<T>> inline constexpr 
bool is_definitely_less_than(const T a, const T b, const T tolerance = default_tolerance<T>) noexcept
{
    return (b - a) > ((std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * tolerance);
}



template<typename T, typename = Test_floating_t<T>> inline constexpr 
bool is_within_tolerance(const T a, const T b, const T tolerance = default_tolerance<T>) noexcept
{
    return std::fabs(a - b) <= tolerance;
}



#endif

//is_approximately_equal(0.101, 0.1, 0.01); // true
//is_essentialy_equal(0.1, 0.1, 0.01); // true
//is_essentialy_equal(0.101, 0.1, 0.01); // false
//is_essentialy_equal(0.101, 0.101, 0.01); // true
//is_approximately_equal(1, 1); // No instance matches arg...

答案 20 :(得分:0)

我使用此代码:

from PyQt4 import QtGui, QtCore


class DockTitleBar(QtGui.QFrame):
    def __init__(self, parent):
        super(DockTitleBar, self).__init__(parent)

        # Is this the only way to give the title bar a border?
        self.setFrameStyle(QtGui.QFrame.Raised | QtGui.QFrame.StyledPanel)

        # Layout for title box
        layout = QtGui.QHBoxLayout(self)
        layout.setSpacing(1)
        layout.setMargin(1)

        self.label = QtGui.QLabel(parent.windowTitle())

        icon_size = QtGui.QApplication.style().standardIcon(
            QtGui.QStyle.SP_TitleBarNormalButton).actualSize(
                QtCore.QSize(100, 100))
        button_size = icon_size + QtCore.QSize(5, 5)

        # Custom button I want to add
        self.button = QtGui.QToolButton(self)
        self.button.setAutoRaise(True)
        self.button.setMaximumSize(button_size)
        self.button.setIcon(QtGui.QApplication.style().standardIcon(
            QtGui.QStyle.SP_TitleBarContextHelpButton))
        self.button.clicked.connect(self.do_something)

        # Close dock button
        self.close_button = QtGui.QToolButton(self)
        self.close_button.setAutoRaise(True)
        self.close_button.setMaximumSize(button_size)
        self.close_button.setIcon(QtGui.QApplication.style().standardIcon(
            QtGui.QStyle.SP_DockWidgetCloseButton))
        self.close_button.clicked.connect(self.close_parent)

        # Setup layout
        layout.addWidget(self.label)
        layout.addStretch()
        layout.addWidget(self.button)
        layout.addWidget(self.close_button)

    def do_something(self):
        # Do something when custom button is pressed
        pass

    def close_parent(self):
        self.parent().hide()

答案 21 :(得分:0)

就数量而言:

如果epsilon是某种物理意义上数量(即相对值)的一小部分,AB类型在相同意义上是相当的,那么认为,以下是完全正确的:

#include <limits>
#include <iomanip>
#include <iostream>

#include <cmath>
#include <cstdlib>
#include <cassert>

template< typename A, typename B >
inline
bool close_enough(A const & a, B const & b,
                  typename std::common_type< A, B >::type const & epsilon)
{
    using std::isless;
    assert(isless(0, epsilon)); // epsilon is a part of the whole quantity
    assert(isless(epsilon, 1));
    using std::abs;
    auto const delta = abs(a - b);
    auto const x = abs(a);
    auto const y = abs(b);
    // comparable generally and |a - b| < eps * (|a| + |b|) / 2
    return isless(epsilon * y, x) && isless(epsilon * x, y) && isless((delta + delta) / (x + y), epsilon);
}

int main()
{
    std::cout << std::boolalpha << close_enough(0.9, 1.0, 0.1) << std::endl;
    std::cout << std::boolalpha << close_enough(1.0, 1.1, 0.1) << std::endl;
    std::cout << std::boolalpha << close_enough(1.1,    1.2,    0.01) << std::endl;
    std::cout << std::boolalpha << close_enough(1.0001, 1.0002, 0.01) << std::endl;
    std::cout << std::boolalpha << close_enough(1.0, 0.01, 0.1) << std::endl;
    return EXIT_SUCCESS;
}

答案 22 :(得分:0)

在数值软件中,您确实需要检查两个浮点数是否完全相等。我在类似的问题上发布了这个

https://stackoverflow.com/a/10973098/1447411

所以你不能说“CompareDoubles1”一般都是错误的。

答案 23 :(得分:0)

我对涉及浮点减法的任何这些答案都非常警惕(例如,fabs(a-b)&lt; epsilon)。首先,浮点数在更大的数量上变得更稀疏,并且在间距大于epsilon的足够高的数量上,你可能只是做一个== b。其次,减去两个非常接近的浮点数(因为这些数字往往是,因为你正在寻找接近相等),这正是你获得catastrophic cancellation的原因。

虽然不便携,但我认为格罗姆的答案可以最好地避免这些问题。

答案 24 :(得分:-1)

/// testing whether two doubles are almost equal. We consider two doubles
/// equal if the difference is within the range [0, epsilon).
///
/// epsilon: a positive number (supposed to be small)
///
/// if either x or y is 0, then we are comparing the absolute difference to
/// epsilon.
/// if both x and y are non-zero, then we are comparing the relative difference
/// to epsilon.
bool almost_equal(double x, double y, double epsilon)
{
    double diff = x - y;
    if (x != 0 && y != 0){
        diff = diff/y; 
    }

    if (diff < epsilon && -1.0*diff < epsilon){
        return true;
    }
    return false;
}

我将这个功能用于我的小项目并且它有效,但请注意以下内容:

双精度错误可能会给您带来惊喜。假设epsilon = 1.0e-6,那么根据上面的代码,1.0和1.000001不应该被认为是相等的,但是在我的机器上,函数认为它们是相等的,这是因为1.000001不能精确地转换为二进制格式,它可能是1.0000009xxx。我用1.0和1.0000011测试它,这次我得到了预期的结果。

答案 25 :(得分:-2)

为什么不执行按位异或?如果相应的位相等,则两个浮点数相等。我认为,决定将指数位置于尾数之前是为了加快两个浮点数的比较。 我想,这里的许多答案都缺少了epsilon比较的观点。 Epsilon值仅取决于比较精确浮点数。例如,在使用浮点数进行一些算术运算后,您会得到两个数字:2.5642943554342和2.5642943554345。它们不相等,但对于解决方案只有3个十进制数字很重要,所以它们是相等的:2.564和2.564。在这种情况下,您选择epsilon等于0.001。按位XOR也可以进行Epsilon比较。如果我错了,请纠正我。

答案 26 :(得分:-2)

以更通用的方式:

template <typename T>
bool compareNumber(const T& a, const T& b) {
    return std::abs(a - b) < std::numeric_limits<T>::epsilon();
}

答案 27 :(得分:-2)

您无法将两个double与固定EPSILON进行比较。根据{{​​1}}的值,double会有所不同。

更好的双重比较是:

EPSILON

答案 28 :(得分:-2)

我的方式可能不正确但有用

将float转换为字符串,然后执行字符串比较

bool IsFlaotEqual(float a, float b, int decimal)
{
    TCHAR form[50] = _T("");
    _stprintf(form, _T("%%.%df"), decimal);


    TCHAR a1[30] = _T(""), a2[30] = _T("");
    _stprintf(a1, form, a);
    _stprintf(a2, form, b);

    if( _tcscmp(a1, a2) == 0 )
        return true;

    return false;

}

运营商重叠也可以完成

答案 29 :(得分:-2)

这是lambda的另一种解决方案:

#include <cmath>
#include <limits>

auto Compare = [](float a, float b, float epsilon = std::numeric_limits<float>::epsilon()){ return (std::fabs(a - b) <= epsilon); };

答案 30 :(得分:-2)

怎么样?

template<typename T>
bool FloatingPointEqual( T a, T b ) { return !(a < b) && !(b < a); }

我见过各种方法-但从未见过,所以我也很想听到任何评论!

答案 31 :(得分:-2)

以下方法比较两个值(在您的情况下为浮点数)的与系统相关的“字符串表示形式”。类似于将它们都打印出来并用肉眼看它们是否相同时:

#include <iostream>
#include <string>

bool floatApproximatelyEquals(const float a, const float b) {
    return std::to_string(a) == std::to_string(b);
}

过程:

  • 有效地考虑了数字因数(或功率),因此数字是否像1.2或1.2e345678或0.00000123或1.2e-345678一样(绝对epsil通常会遇到的问题)

缺点:

  • 您无法控制“舍入”数字的精度。 F.e.在我的系统上,它是第一个有效数字(非零)之后的6位数字(以十进制表示)(对于我的大多数情况已经足够了)