如何最好地比较IEEE float和double的相等性?我听说过几种方法,但我想知道社区的想法。
如何最好地比较IEEE float和double的相等性?我听说过几种方法,但我想知道社区的想法。
我认为最好的方法是比较 ULPs。
bool is_nan(float f)
{
return (*reinterpret_cast<unsigned __int32*>(&f) & 0x7f800000) == 0x7f800000 && (*reinterpret_cast<unsigned __int32*>(&f) & 0x007fffff) != 0;
}
bool is_finite(float f)
{
return (*reinterpret_cast<unsigned __int32*>(&f) & 0x7f800000) != 0x7f800000;
}
// if this symbol is defined, NaNs are never equal to anything (as is normal in IEEE floating point)
// if this symbol is not defined, NaNs are hugely different from regular numbers, but might be equal to each other
#define UNEQUAL_NANS 1
// if this symbol is defined, infinites are never equal to finite numbers (as they're unimaginably greater)
// if this symbol is not defined, infinities are 1 ULP away from +/- FLT_MAX
#define INFINITE_INFINITIES 1
// test whether two IEEE floats are within a specified number of representable values of each other
// This depends on the fact that IEEE floats are properly ordered when treated as signed magnitude integers
bool equal_float(float lhs, float rhs, unsigned __int32 max_ulp_difference)
{
#ifdef UNEQUAL_NANS
if(is_nan(lhs) || is_nan(rhs))
{
return false;
}
#endif
#ifdef INFINITE_INFINITIES
if((is_finite(lhs) && !is_finite(rhs)) || (!is_finite(lhs) && is_finite(rhs)))
{
return false;
}
#endif
signed __int32 left(*reinterpret_cast<signed __int32*>(&lhs));
// transform signed magnitude ints into 2s complement signed ints
if(left < 0)
{
left = 0x80000000 - left;
}
signed __int32 right(*reinterpret_cast<signed __int32*>(&rhs));
// transform signed magnitude ints into 2s complement signed ints
if(right < 0)
{
right = 0x80000000 - right;
}
if(static_cast<unsigned __int32>(std::abs(left - right)) <= max_ulp_difference)
{
return true;
}
return false;
}
类似的技巧也可以用于双精度浮点数。关键是将浮点数转换为有序的(就像整数一样),然后看它们之间的差异。
我不知道为什么这个该死的东西会弄乱我的下划线。编辑:哦,也许这只是预览的副作用。那就没事了。
bool is_equals(float A, float B,
float maxRelativeError, float maxAbsoluteError)
{
if (fabs(A - B) < maxAbsoluteError)
return true;
float relativeError;
if (fabs(B) > fabs(A))
relativeError = fabs((A - B) / B);
else
relativeError = fabs((A - B) / A);
if (relativeError <= maxRelativeError)
return true;
return false;
}
通过相对误差和绝对误差容限的组合,这似乎解决了大多数问题。那么 ULP 方法更好吗?如果是,为什么?
P.S.:
当然有很多情况下你不想检查“是否完全相等”。对于许多人来说,这甚至可能是他们需要处理的唯一情况。我想指出的是还有其他情况。
虽然 LAPACK 是用 Fortran 编写的,但如果您在使用其他编程语言进行数值软件开发时,逻辑是相同的。
#include <iostream>
using namespace std;
int main()
{
float a = 1.0;
float b = 0.0;
for(int i=0;i<10;++i)
{
b+=0.1;
}
if(a != b)
{
cout << "Something is wrong" << endl;
}
return 1;
}
打印出短语“Something is wrong”。你是在说它应该这样吗?
非常烦人,最终我放弃了整个尝试,只依赖浮点数和<和>来完成工作。虽不完美,但能满足预期用例。
哦,亲爱的上帝,请不要将浮点位解释为整数,除非您正在运行 P6 或更早版本。
(a==b || (a!=a && b != b))
比任何涉及浮点位到整数转换的操作都要快,尽管像上面这样的表达式确实让我感到痛苦。我想知道 NaN!=NaN 决策提供了多少好处,以及它在浪费代码和调试时间方面造成了多少代价? - supercat通过结合相对误差和绝对误差容限,这似乎解决了大多数问题。ULP方法更好吗?如果是,为什么?
ULP是两个浮点数之间“距离”的直接度量。这意味着它们不需要您想出相对和绝对误差值,也不需要确保获得那些值“准确无误”。使用ULP,您可以直接表达您希望数字之间的接近程度,并且相同的阈值对于小值和大值同样有效。