浮点数和双精度比较最有效的方法是什么?

比较两个doublefloat值的最有效方法是什么?

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

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

比如:

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

似乎是浪费加工。

有人知道更聪明的浮点比较器吗?

590520 次浏览

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

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

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

编辑:克里斯特在最近的博客文章上添加了关于这个主题的大量信息。享受。

你写的代码有bug:

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

正确的代码应该是:

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

(…是的,这是不同的)

我想知道晶圆厂是否会让你在某些情况下失去懒惰的评价。我会说这取决于编译器。你可能想两种都试试。如果它们在平均水平上是相等的,则采用晶圆厂实现。

如果你有一些关于两个浮点数中哪一个比另一个更大的信息,你可以根据比较的顺序来更好地利用惰性求值。

最后,通过内联这个函数可能会得到更好的结果。不过不太可能有太大改善……

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

要了解更深入的方法,请阅读比较浮点数。以下是该链接的代码片段:

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

这取决于你想要的比较有多精确。如果您想对完全相同的数字进行比较,那么只需使用==。(除非你真的想要完全相同的数字,否则你几乎不会想这么做。)在任何一个不错的平台上,你都可以做到以下几点:

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

因为fabs的速度非常快。我说的快是指它基本上是一个位与,所以它最好快。

用于比较双精度和浮点数的整数技巧很好,但往往会使各种CPU管道更难有效处理。现在,由于使用堆栈作为频繁使用的值的临时存储区域,在某些有序架构上它肯定不会更快。(在乎的人可以去Load-hit-store。)

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

' return fabs(a - b) <ε;

这是可以的,如果:

  • 输入的数量级变化不大
  • 极少数相反的符号可以被视为相等

否则就会给你带来麻烦。双精度数的分辨率约为小数点后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就足够了,那就使用它吧!但也要注意它的缺点和其他可能的解决方案。

在c++中获取的可移植方法是

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

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

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

  1. 算法中的隐式假设,如果a==bb==c,则a==c

  2. 使用相同的epsilon来测量以英寸为单位的线和以mils为单位的线(。001英寸)。这是a==b,而是1000a!=1000b。(这就是为什么AlmostEqual2sComplement要求epsilon或max ULPS)。

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

  4. 使用比较函数对集合中的项进行排序。(在这种情况下,使用内置的c++操作符==来进行双精度操作可以产生正确的结果。)

就像我说的:这一切都取决于环境和ab的预期大小。

顺便说一下,std::numeric_limits<double>::epsilon()是“机器epsilon”。它是1.0和下一个可由double表示的值之间的差值。我猜它可以用在比较函数中,但只有当期望值小于1时。(这是对@cdv的回答的回应…)

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

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

Bruce Dawson编写的比较浮点数是研究浮点比较的一个好起点。

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

当然,选择取决于上下文,并决定你想要的数字有多相等。

比较浮点数的另一种方法是查看数字的ULP(最后位置的单位)。虽然没有专门处理比较,但是论文每个计算机科学家都应该知道的关于浮点数的知识是一个很好的资源,可以帮助您理解浮点数是如何工作的以及有哪些缺陷,包括什么是ULP。

我发现谷歌c++测试框架包含了一个很好的基于模板的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中提取了下面的代码,并在上面添加了许可证。

一定要将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年前写的。它可能仍然有效,代码也很好,但有些人发现了改进。最好从谷歌测试源代码中获得AlmostEquals的最新版本,而不是我粘贴在这里的那个。

我对任何涉及浮点减法的答案都非常谨慎(例如,fabs(a-b) <ε)。首先,浮点数在更大的量级上变得更稀疏,在足够大的量级上,当间隔大于时,您可能只需要做a == b。其次,减去两个非常接近的浮点数(考虑到您正在寻找接近相等的情况,这将趋于如此)正是您得到灾难性的取消的方式。

虽然不能移植,但我认为grom的答案在避免这些问题方面做得最好。

不幸的是,即使您的“浪费”代码也是不正确的。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)))
{
// ...
}

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

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

同样地,当两个值的大小接近ε时,相对误差增长到100%。

因此,尝试将0.00001这样的固定点数与浮点值(其中指数是任意的)混合使用是毫无意义的。只有当您能够确保操作数值位于一个狭窄的域中(即接近某个特定的指数),并且为特定的测试正确地选择了一个epsilon值时,这种方法才会起作用。如果你从空气中抽出一个数字(“嘿!0.00001很小,所以这一定很好!”),你注定会出现数字错误。我花了大量的时间调试糟糕的数值代码,其中一些可怜的笨蛋抛出随机的epsilon值,以使另一个测试用例工作。

如果你做任何类型的数值编程并且相信你需要得到定点,阅读bruce关于比较浮点数的文章

浮点数比较 .

我的课程是基于之前发布的答案。非常类似于谷歌的代码,但我使用了一个偏差,将所有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 );
}
};

在数值软件中,确实有这样的情况,您需要检查两个浮点数完全是否相等。我就一个类似的问题发表了这篇文章

https://stackoverflow.com/a/10973098/1447411 < a href = " https://stackoverflow.com/a/10973098/1447411 " > < / >

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

意识到这是一个老话题,但这篇文章是我发现的比较浮点数的最直接的文章之一,如果你想探索更多,它有更详细的参考资料,它的主站点涵盖了处理浮点数浮点指南:比较的完整范围。

我们可以在重新讨论浮点公差中找到一篇更实用的文章,并指出有绝对的宽容测试,在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 ;
}

就数量的规模而言:

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

我的方法也许不正确,但很有用

将两个浮点数都转换为字符串,然后进行字符串比较

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;


}

也可以做到操作人员超载

/// 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测试了它,这次我得到了预期的结果。

你不能比较两个double和一个固定的EPSILON。根据double的值,EPSILON会发生变化。

更好的双比较应该是:

bool same(double a, double b)
{
return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b
&& std::nextafter(a, std::numeric_limits<double>::max()) >= b;
}

以更一般的方式:

template <typename T>
bool compareNumber(const T& a, const T& b) {
return std::abs(a - b) < std::numeric_limits<T>::epsilon();
}
< p >注意:< br > 正如@SirGuy所指出的,这种方法是有缺陷的。 我把这个答案留在这里作为一个例子,不遵循

为什么不执行位异或?如果两个浮点数对应的位相等,则两个浮点数相等。我认为,把指数位放在尾数之前的决定是为了加快两个浮点数的比较。 我认为,这里的很多答案都忽略了比较的重点。Epsilon值只取决于与浮点数比较的精度。例如,在用浮点数做一些算术之后,你会得到两个数字:2.5642943554342和2.5642943554345。它们不相等,但对于解决方案,只有3个十进制数字重要,所以它们相等:2.564和2.564。在这种情况下,你选择= 0.001。Epsilon比较也可以用位异或进行。

.

.

我最终花了相当多的时间在这个伟大的线程通过材料。我怀疑每个人都想花这么多时间,所以我将强调我所学到的总结和我实施的解决方案。

快速的总结

  1. 1e-8和1e-16差不多吗?如果你在看有噪声的传感器数据,那么可能是,但如果你在做分子模拟,那么可能不是!底线:你总是需要在特定函数调用的上下文中考虑宽容值,而不仅仅是让它成为应用范围内的通用硬编码常量。
  2. 对于一般的库函数,使用默认的公差作为形参仍然很好。一个典型的选择是numeric_limits::epsilon(),它与float.h中的FLT_EPSILON相同。然而,这是有问题的,因为比较像1.0这样的值和比较像1E9这样的值是不一样的。FLT_EPSILON定义为1.0。
  3. 检查number是否在公差范围内的明显实现是fabs(a-b) <= epsilon,但这不起作用,因为默认的epsilon定义为1.0。我们需要用a和b来上下缩放。
  4. 这个问题有两种解决方案:要么你设置成正比于max(a,b),要么你可以在a周围得到下一个可表示的数字,然后看看b是否在这个范围内。前者称为“相对”法,后者称为ULP法。
  5. 当与0比较时,这两个方法实际上都失败了。在这种情况下,应用程序必须提供正确的公差。

实用函数实现(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;
}

我使用这个代码:

bool AlmostEqual(double v1, double v2)
{
return (std::fabs(v1 - v2) < std::fabs(std::min(v1, v2)) * std::numeric_limits<double>::epsilon());
}

我为java编写这篇文章,但是您可能会发现它很有用。它使用长变量而不是双变量,但会处理nan、亚法线等。

public static boolean equal(double a, double b) {
final long fm = 0xFFFFFFFFFFFFFL;       // fraction mask
final long sm = 0x8000000000000000L;    // sign mask
final long cm = 0x8000000000000L;       // most significant decimal bit mask
long c = Double.doubleToLongBits(a), d = Double.doubleToLongBits(b);
int ea = (int) (c >> 52 & 2047), eb = (int) (d >> 52 & 2047);
if (ea == 2047 && (c & fm) != 0 || eb == 2047 && (d & fm) != 0) return false;   // NaN
if (c == d) return true;                            // identical - fast check
if (ea == 0 && eb == 0) return true;                // ±0 or subnormals
if ((c & sm) != (d & sm)) return false;             // different signs
if (abs(ea - eb) > 1) return false;                 // b > 2*a or a > 2*b
d <<= 12; c <<= 12;
if (ea < eb) c = c >> 1 | sm;
else if (ea > eb) d = d >> 1 | sm;
c -= d;
return c < 65536 && c > -65536;     // don't use abs(), because:
// There is a posibility c=0x8000000000000000 which cannot be converted to positive
}
public static boolean zero(double a) { return (Double.doubleToLongBits(a) >> 52 & 2047) < 3; }

请记住,在一些浮点运算之后,number可能与我们期望的非常不同。没有代码可以解决这个问题。

下面是使用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且仅大于1),两个输入值尽可能接近,并且仍然比较为不接近。因此,对于大于1.0的值,不妨只使用相等性测试。当比较浮点值时,固定的epsilon将无法保存您的数据。

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 值。< / p >
static inline bool qFuzzyIsNull(double d)
{
return qAbs(d) <= 0.000000000001;
}


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

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

这是另一个解:

#include <cmath>
#include <limits>


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

这个怎么样?

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

我见过各种方法,但从来没有见过这个,所以我也很好奇听到任何评论!

在这个版本中,你可以检查,这些数字之间的差异并不比某些分数(比如,0.0001%)更大:

bool floatApproximatelyEquals(const float a, const float b) {
if (b == 0.) return a == 0.; // preventing division by zero
return abs(1. - a / b) < 1e-6;
}

请注意Sneftel关于浮动可能的分数限制的评论。

还要注意的是,它不同于使用绝对ε的方法——在这里你不用担心“数量级”。-数字可能是1e100,或者1e-100,它们将始终一致地进行比较,你不必为每个情况更新epsilon。

你必须为浮点数比较做这个处理,因为浮点数不能像整数类型那样完美地比较。下面是各种比较运算符的函数。

浮点数等于(==)

我也更喜欢减法技术,而不是依赖于fabs()abs(),但我必须在从64位PC到ATMega328微控制器(Arduino)的各种架构上快速配置它,以真正了解它是否有很大的性能差异。

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

微软的例子修改:

/// @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);

我不完全确定,但在我看来,对于基于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);

通过这种方式,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;
}

额外的改进:

    在c++中,epsilon的一个很好的默认值是std::numeric_limits<T>::epsilon(),它的计算结果是0FLT_EPSILONDBL_EPSILONLDBL_EPSILON。看这里:std::numeric_limits<T>::epsilon()0。您还可以看到FLT_EPSILONDBL_EPSILONLDBL_EPSILONfloat.h标题。
    1. 参见https://en.cppreference.com/w/cpp/header/cfloat
    2. https://www.cplusplus.com/reference/cfloat/
  1. 你可以用模板代替函数,来处理所有浮点类型:floatdouble,和long double对这些类型进行类型检查,通过模板中的static_assert()
  2. 缩放epsilon值是一个好主意,以确保它适用于非常大和非常小的ab值。这篇文章推荐并解释了a0。因此,您应该按等于max(1.0, abs(a), abs(b))的缩放值缩放epsilon,正如那篇文章解释的那样。否则,随着a和/或b的大小增加,epsilon最终会相对于这些值变得非常小,以至于在浮点误差中消失。所以,我们把它放大,就像它们一样。然而,使用1.0作为epsilon允许的最小比例因子还可以确保对于非常小的数值ab, epsilon本身不会缩放到小到在浮点误差中丢失的程度。因此,我们将最小缩放因子限制为1.0
  3. 如果你想要“封装”;把上面的函数转换成一个类,不要。相反,如果您愿意,可以将它们打包到一个名称空间中,以便命名它们。例如:如果您将所有独立函数放到一个名为float_comparison的名称空间中,那么您可以像这样访问is_eq()函数,例如:float_comparison::is_eq(1.0, 1.5);
  4. 添加对0的比较,而不仅仅是两个值之间的比较,可能也会很好。
  5. 所以,这里是一个更好的解决方案类型与上述改进到位:
    namespace float_comparison {
    
    
    /// Scale the epsilon value to become large for large-magnitude a or b,
    /// but no smaller than 1.0, per the explanation above, to ensure that
    /// epsilon doesn't ever fall out in floating point error as a and/or b
    /// increase in magnitude.
    template<typename T>
    static constexpr T scale_epsilon(T a, T b, T epsilon =
    std::numeric_limits<T>::epsilon()) noexcept
    {
    static_assert(std::is_floating_point_v<T>, "Floating point comparisons "
    "require type float, double, or long double.");
    T scaling_factor;
    // Special case for when a or b is infinity
    if (std::isinf(a) || std::isinf(b))
    {
    scaling_factor = 0;
    }
    else
    {
    scaling_factor = std::max({(T)1.0, std::abs(a), std::abs(b)});
    }
    
    
    T epsilon_scaled = scaling_factor * std::abs(epsilon);
    return epsilon_scaled;
    }
    
    
    // Compare two values
    
    
    /// Equal: returns true if a is approximately == b, and false otherwise
    template<typename T>
    static constexpr bool is_eq(T a, T b, T epsilon =
    std::numeric_limits<T>::epsilon()) noexcept
    {
    static_assert(std::is_floating_point_v<T>, "Floating point comparisons "
    "require type float, double, or long double.");
    // test `a == b` first to see if both a and b are either infinity
    // or -infinity
    return a == b || std::abs(a - b) <= scale_epsilon(a, b, epsilon);
    }
    
    
    /*
    etc. etc.:
    is_eq()
    is_ne()
    is_lt()
    is_le()
    is_gt()
    is_ge()
    */
    
    
    // Compare against zero
    
    
    /// Equal: returns true if a is approximately == 0, and false otherwise
    template<typename T>
    static constexpr bool is_eq_zero(T a, T epsilon =
    std::numeric_limits<T>::epsilon()) noexcept
    {
    static_assert(std::is_floating_point_v<T>, "Floating point comparisons "
    "require type float, double, or long double.");
    return is_eq(a, (T)0.0, epsilon);
    }
    
    
    /*
    etc. etc.:
    is_eq_zero()
    is_ne_zero()
    is_lt_zero()
    is_le_zero()
    is_gt_zero()
    is_ge_zero()
    */
    
    
    } // namespace float_comparison
    

参见:

    上面的一些函数的宏形式在我的repo这里:utilities.h
    1. 2020年11月29日更新:这是一个正在进行的工作,我将在准备就绪时将其作为一个单独的答案,但我已经在这个文件中生成了一个更好的、缩放的C中所有函数的epsilon版本:utilities.c。来看看。
  1. 更多的阅读我现在已经完成了:浮点公差的重新审视,克里斯特·埃里克森。非常有用的文章!它讨论了缩放epsilon,以确保它永远不会出现浮点错误,即使对于非常大的a和/或b值!

我使用这个代码。不像上面的答案,这允许一个人 给出一个在代码注释中解释的abs_relative_error

第一个版本比较复数,使错误 可以用两个“矢量”之间的夹角来解释; 在复平面上具有相同的长度(这给出了一点 洞察力)。然后是2实数的正确公式 数字。< / p >

https://github.com/CarloWood/ai-utils/blob/master/almost_equal.h

后者是

template<class T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
almost_equal(T x, T y, T const abs_relative_error)
{
return 2 * std::abs(x - y) <= abs_relative_error * std::abs(x + y);
}

其中abs_relative_error基本上(两倍)是文献中最接近定义的绝对值:相对误差。但这只是名字的选择。

我认为在复平面中最明显的是。如果|x| = 1, y在x周围形成一个直径为abs_relative_error的圆,则认为两者相等。

我使用以下函数进行浮点数比较:

bool approximatelyEqual(double a, double b)
{
return fabs(a - b) <= ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * std::numeric_limits<double>::epsilon());
}