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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17333/
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 12:15:33  来源:igfitidea点击:

What is the most effective way for float and double comparison?

c++algorithmoptimizationfloating-point

提问by Alex

What would be the most efficient way to compare two doubleor two floatvalues?

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

Simply doing this is not correct:

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

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

But something like:

但像:

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

Seems to waste processing.

好像是浪费处理。

Does anyone know a smarter float comparer?

有谁知道更智能的浮点比较器?

回答by Andrew Stein

Be extremely careful using any of the other suggestions. It all depends on context.

使用任何其他建议时要非常小心。这一切都取决于上下文。

I have spent a long time tracing a bugs in a system that presumed a==bif |a-b|<epsilon. The underlying problems were:

我花了很长一段时间的跟踪在假定的系统中的漏洞a==b,如果|a-b|<epsilon。潜在的问题是:

  1. The implicit presumption in an algorithm that if a==band b==cthen a==c.

  2. Using the same epsilon for lines measured in inches and lines measured in mils (.001 inch). That is a==bbut 1000a!=1000b. (This is why AlmostEqual2sComplement asks for the epsilon or max ULPS).

  3. The use of the same epsilon for both the cosine of angles and the length of lines!

  4. Using such a compare function to sort items in a collection. (In this case using the builtin C++ operator == for doubles produced correct results.)

  1. 算法中的隐式假设,即 ifa==bb==cthen a==c

  2. 对以英寸为单位测量的线和以密耳(0.001 英寸)为单位的线使用相同的 epsilon。那a==b不过1000a!=1000b。(这就是AlmostEqual2sComplement 要求epsilon 或max ULPS 的原因)。

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

  4. 使用这样的比较函数对集合中的项目进行排序。(在这种情况下,对双精度使用内置 C++ 运算符 == 会产生正确的结果。)

Like I said: it all depends on context and the expected size of aand b.

就像我说:这一切都取决于背景和预期的大小ab

BTW, std::numeric_limits<double>::epsilon()is the "machine epsilon". It is the difference between 1.0 and the next value representable by a double. I guess that it could be used in the compare function but only if the expected values are less than 1. (This is in response to @cdv's answer...)

顺便说一句,std::numeric_limits<double>::epsilon()是“机器epsilon”。它是 1.0 和下一个可以用双精度表示的值之间的差值。我想它可以在比较函数中使用,但前提是预期值小于 1。(这是对@cdv 的回答的回应......)

Also, if you basically have intarithmetic in doubles(here we use doubles to hold int values in certain cases) your arithmetic will be correct. For example 4.0/2.0 will be the same as 1.0+1.0. This is as long as you do not do things that result in fractions (4.0/3.0) or do not go outside of the size of an int.

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

回答by OJ.

The comparison with an epsilon value is what most people do (even in game programming).

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

You should change your implementation a little though:

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

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


Edit: Christer has added a stack of great info on this topic on a recent blog post. Enjoy.

编辑:Christer 在最近的一篇博客文章中添加了大量关于此主题的重要信息。享受。

回答by skrebbel

I found that the Google C++ Testing Frameworkcontains a nice cross-platform template-based implementation of AlmostEqual2sComplement which works on both doubles and floats. Given that it is released under the BSD license, using it in your own code should be no problem, as long as you retain the license. I extracted the below code from http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.hhttps://github.com/google/googletest/blob/master/googletest/include/gtest/internal/gtest-internal.hand added the license on top.

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

Be sure to #define GTEST_OS_WINDOWS to some value (or to change the code where it's used to something that fits your codebase - it's BSD licensed after all).

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

Usage example:

用法示例:

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

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

Here's the code:

这是代码:

// 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: [email protected] (Zhanyong Wan), [email protected] (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_;
};

EDIT: This post is 4 years old. It's probably still valid, and the code is nice, but some people found improvements. Best go get the latest version of AlmostEqualsright from the Google Test source code, and not the one I pasted up here.

编辑:这篇文章已有 4 年历史。它可能仍然有效,并且代码很好,但有些人发现了改进。最好AlmostEquals从 Google Test 源代码中获取最新版本的right,而不是我在这里粘贴的那个。

回答by mch

Comparing floating point numbers for depends on the context. Since even changing the order of operations can produce different results, it is important to know how "equal" you want the numbers to be.

比较浮点数取决于上下文。由于即使更改操作顺序也会产生不同的结果,因此了解您希望数字的“相等”程度很重要。

Comparing floating point numbersby Bruce Dawson is a good place to start when looking at floating point comparison.

在查看浮点比较时,Bruce Dawson 的《比较浮点数》是一个很好的起点。

The following definitions are from The art of computer programming by Knuth:

以下定义来自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);
}

Of course, choosing epsilon depends on the context, and determines how equal you want the numbers to be.

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

Another method of comparing floating point numbers is to look at the ULP (units in last place) of the numbers. While not dealing specifically with comparisons, the paper What every computer scientist should know about floating point numbersis a good resource for understanding how floating point works and what the pitfalls are, including what ULP is.

比较浮点数的另一种方法是查看数字的 ULP(最后一位的单位)。虽然没有专门处理比较,但论文“每个计算机科学家应该了解浮点数的知识”是了解浮点数的工作原理以及陷阱是什么(包括 ULP 是什么)的好资源。

回答by grom

For a more in depth approach read Comparing floating point numbers. Here is the code snippet from that link:

如需更深入的方法,请阅读比较浮点数。这是该链接中的代码片段:

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

回答by Shafik Yaghmour

Realizing this is an old thread but this article is one of the most straight forward ones I have found on comparing floating point numbers and if you want to explore more it has more detailed references as well and it the main site covers a complete range of issues dealing with floating point numbers The Floating-Point Guide :Comparison.

意识到这是一个旧线程,但这篇文章是我在比较浮点数时发现的最直接的文章之一,如果您想探索更多,它也有更详细的参考,并且主站点涵盖了完整的问题范围处理浮点数浮点指南:比较

We can find a somewhat more practical article in Floating-point tolerances revisitedand notes there is absolute tolerancetest, which boils down to this in C++:

我们可以在重新访问浮点容差中找到一篇更实用的文章,并指出有绝对容差测试,这在 C++ 中归结为:

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

and relative tolerancetest:

相对容差测试:

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

The article notes that the absolute test fails when xand yare large and fails in the relative case when they are small. Assuming he absolute and relative tolerance is the same a combined test would look like this:

文章指出,绝对的测试失败时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 ;
}

回答by Chris de Vries

The portable way to get epsilon in C++ is

在 C++ 中获取 epsilon 的可移植方式是

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

Then the comparison function becomes

那么比较函数就变成了

#include <cmath>
#include <limits>

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

回答by Shital Shah

I ended up spending quite some time going through material in this great thread. I doubt everyone wants to spend so much time so I would highlight the summary of what I learned and the solution I implemented.

我最终花了相当多的时间来浏览这个很棒的线程中的材料。我怀疑每个人都愿意花这么多时间,所以我将重点介绍我所学到的知识和我实施的解决方案的总结。

Quick Summary

快速总结

  1. Is 1e-8 approximately same as 1e-16? If you are looking at noisy sensor data then probably yes but if you are doing molecular simulation then may be not! Bottom line: You always need to think of tolerancevalue in context of specific function call and not just make it generic app-wide hard-coded constant.
  2. For general library functions, it's still nice to have parameter with default tolerance. A typical choice is numeric_limits::epsilon()which is same as FLT_EPSILON in float.h. This is however problematic because epsilon for comparing values like 1.0 is not same as epsilon for values like 1E9. The FLT_EPSILON is defined for 1.0.
  3. The obvious implementation to check if number is within tolerance is fabs(a-b) <= epsilonhowever this doesn't work because default epsilon is defined for 1.0. We need to scale epsilon up or down in terms of a and b.
  4. There are two solution to this problem: either you set epsilon proportional to max(a,b)or you can get next representable numbers around a and then see if b falls into that range. The former is called "relative" method and later is called ULP method.
  5. Both methods actually fails anyway when comparing with 0. In this case, application must supply correct tolerance.
  1. 1e-8 与 1e-16 大致相同吗?如果您正在查看嘈杂的传感器数据,那么可能是,但如果您正在进行分子模拟,则可能不是!底线:您始终需要在特定函数调用的上下文中考虑差值,而不仅仅是使其成为应用程序范围内的通用硬编码常量。
  2. 对于一般的库函数,参数默认的tolerance还是不错的。一个典型的选择是numeric_limits::epsilon()与 float.h 中的 FLT_EPSILON 相同。然而,这是有问题的,因为用于比较 1.0 等值的 epsilon 与用于 1E9 等值的 epsilon 不同。FLT_EPSILON 是为 1.0 定义的。
  3. 检查数字是否在容差范围内的明显实现是fabs(a-b) <= epsilon但是这不起作用,因为默认 epsilon 是为 1.0 定义的。我们需要根据 a 和 b 放大或缩小 epsilon。
  4. 这个问题有两种解决方案:要么将 epsilon 设置为成正比,max(a,b)要么可以在 a 周围获得下一个可表示的数字,然后查看 b 是否落在该范围内。前者称为“相对”法,后称为 ULP 法。
  5. 与 0 比较时,这两种方法实际上都失败了。在这种情况下,应用程序必须提供正确的容差。

Utility Functions Implementation (C++11)

实用函数实现 (C++11)

//implements relative method - do not use for comparing with zero
//use this most of the time, tolerance needs to be meaningful in your context
template<typename TReal>
static bool isApproximatelyEqual(TReal a, TReal b, TReal tolerance = std::numeric_limits<TReal>::epsilon())
{
    TReal diff = std::fabs(a - b);
    if (diff <= tolerance)
        return true;

    if (diff < std::fmax(std::fabs(a), std::fabs(b)) * tolerance)
        return true;

    return false;
}

//supply tolerance that is meaningful in your context
//for example, default tolerance may not work if you are comparing double with float
template<typename TReal>
static bool isApproximatelyZero(TReal a, TReal tolerance = std::numeric_limits<TReal>::epsilon())
{
    if (std::fabs(a) <= tolerance)
        return true;
    return false;
}


//use this when you want to be on safe side
//for example, don't start rover unless signal is above 1
template<typename TReal>
static bool isDefinitelyLessThan(TReal a, TReal b, TReal tolerance = std::numeric_limits<TReal>::epsilon())
{
    TReal diff = a - b;
    if (diff < tolerance)
        return true;

    if (diff < std::fmax(std::fabs(a), std::fabs(b)) * tolerance)
        return true;

    return false;
}
template<typename TReal>
static bool isDefinitelyGreaterThan(TReal a, TReal b, TReal tolerance = std::numeric_limits<TReal>::epsilon())
{
    TReal diff = a - b;
    if (diff > tolerance)
        return true;

    if (diff > std::fmax(std::fabs(a), std::fabs(b)) * tolerance)
        return true;

    return false;
}

//implements ULP method
//use this when you are only concerned about floating point precision issue
//for example, if you want to see if a is 1.0 by checking if its within
//10 closest representable floating point numbers around 1.0.
template<typename TReal>
static bool isWithinPrecisionInterval(TReal a, TReal b, unsigned int interval_size = 1)
{
    TReal min_a = a - (a - std::nextafter(a, std::numeric_limits<TReal>::lowest())) * interval_size;
    TReal max_a = a + (std::nextafter(a, std::numeric_limits<TReal>::max()) - a) * interval_size;

    return min_a <= b && max_a >= b;
}

回答by fulmicoton

The code you wrote is bugged :

你写的代码有问题:

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

The correct code would be :

正确的代码是:

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

(...and yes this is different)

(......是的,这是不同的)

I wonder if fabs wouldn't make you lose lazy evaluation in some case. I would say it depends on the compiler. You might want to try both. If they are equivalent in average, take the implementation with fabs.

我想知道在某些情况下 fabs 是否不会让您失去懒惰的评估。我会说这取决于编译器。您可能想同时尝试两者。如果它们的平均值相等,则采用晶圆厂实施。

If you have some info on which of the two float is more likely to be bigger than then other, you can play on the order of the comparison to take better advantage of the lazy evaluation.

如果您有一些关于两个浮点数中哪个更可能比另一个更大的信息,您可以按照比较的顺序进行播放,以更好地利用惰性求值。

Finally you might get better result by inlining this function. Not likely to improve much though...

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

Edit: OJ, thanks for correcting your code. I erased my comment accordingly

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

回答by fulmicoton

`return fabs(a - b) < EPSILON;

`返回晶圆厂(a - b)< EPSILON;

This is fine if:

如果:

  • the order of magnitude of your inputs don't change much
  • very small numbers of opposite signs can be treated as equal
  • 您输入的数量级变化不大
  • 极少量的相反符号可以被视为相等

But otherwise it'll lead you into trouble. Double precision numbers have a resolution of about 16 decimal places. If the two numbers you are comparing are larger in magnitude than EPSILON*1.0E16, then you might as well be saying:

但否则它会给你带来麻烦。双精度数的分辨率约为小数点后 16 位。如果您比较的两个数字的数量级大于 EPSILON*1.0E16,那么您可能会说:

return a==b;

I'll examine a different approach that assumes you need to worry about the first issue and assume the second is fine your application. A solution would be something like:

我将研究一种不同的方法,假设您需要担心第一个问题,并假设第二个问题适用于您的应用程序。一个解决方案是这样的:

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

This is expensive computationally, but it is sometimes what is called for. This is what we have to do at my company because we deal with an engineering library and inputs can vary by a few dozen orders of magnitude.

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

Anyway, the point is this (and applies to practically every programming problem): Evaluate what your needs are, then come up with a solution to address your needs -- don't assume the easy answer will address your needs. If after your evaluation you find that fabs(a-b) < EPSILONwill suffice, perfect -- use it! But be aware of its shortcomings and other possible solutions too.

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