比较两个双精度浮点数或两个浮点数最有效的方法是什么?
简单地这样做是不正确的:
bool CompareDoubles1 (double A, double B)
{
return A == B;
}
比如:
bool CompareDoubles2 (double A, double B)
{
diff = A - B;
return (diff < EPSILON) && (-diff < EPSILON);
}
似乎是浪费加工。
有人知道更聪明的浮点比较器吗?
下面是使用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将无法保存您的数据。
在这个版本中,你可以检查,这些数字之间的差异并不比某些分数(比如,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关于浮动可能的分数限制的评论。
还要注意的是,它不同于使用绝对的epsilon的方法——这里你不需要担心“数量级”——数字可能是,比如说1e100,或者1e-100,它们总是会被一致地比较,而且你不必为每一种情况更新epsilon。
我的课程是基于之前发布的答案。非常类似于谷歌的代码,但我使用了一个偏差,将所有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 );
}
};