显然,两个float类型的变量,直接用if(f1==f2)来判定是否相等是不妥的。但应该怎么判断呢?
#define FLOAT_CMP_PRECISION 0.000001f
if(abs(f1-f2)<FLOAT_CMP_RECISION)
return true;
然而,这对于指数不为大于1的数字来说会过于严格,导致许多本来差值在正常误差之内、因而应认为相等的两个数被认为不相等;类似地,对于指数小于-1的数字又过于宽松。
对于常见的几乎无所不在的IEEE754格式的浮点数(见 网友老莫的分析),网友心灵驿站给出了 一个巧妙的高效的判定方法,只使用四次整数比较、1到3次整数减法即可完成比较,真是高效又准确,物美价廉
bool IsEqual(float f1, float f2, int absDelta)
{
int i1, i2;
i1 = ( f1>0) ? ((int&)f1) : ( (int&) f1 - 0x80000000 );
i2 = (f2>0) ? ((int&)f2) : ( (int&) f2 - 0x80000000 );
return ((abs(i1-i2))<absDelta) ? true : false;
}
其原理是利用了IEEE754格式中把符号位与底数写在一起后,满足某种连续性的特征。 然而,
仔细推敲,发现其中是有错误的!减法的被减数和减数弄反了。会导致这样的错误:4.2039e-45(即0x00000003)和 -4.2039e45(0x80000003)也认为相等:(
另外,(int&)f1这种写法在VC2010编译时也会出错:不是把f1的4个字节当做整数来解释获得一个整数,而是效果等同于(int)f1,从1.1f中得到1。
经过考虑和实验,我认为正确的做法应是(下午一度给改错了,希望没有误导人):
///Condition: exponents are same and high (24-FLOAT_CMP_IGNORE_BITS) bits in mantissa are same.
#define FLOAT_CMP_IGNORE_BITS 7
bool FloatEqual(float f1, float f2)
{
int i1 = (*(int*)&f1 & 0x80000000) ? 0x80000000-*(int*)&f1 : *(int*)&f1;
int i2 = (*(int*)&f2 & 0x80000000) ? 0x80000000-*(int*)&f2 : *(int*)&f2;
int diff = i1 - i2;
return -(1<<FLOAT_CMP_IGNORE_BITS) < diff && diff < (1<<FLOAT_CMP_IGNORE_BITS);
}
【原理】
考虑到浮点数判等,只需要做到:在只有当两个浮点数指数相同、底数的高若干bit也相同时才认为两个数相同。 考虑到在IEEE754的巧妙的编码方式下,float的底数部分,即"挖去”中间的8位指数位后剩下的24位,是一个“原码(Sign-Magnitude)"编码的整数,即最高位为1表示是负数,其余位表示绝对值。在我们的场景下,即使不挖去这8位指数,也可以放心地把整个32位都当做一个原码整数。
因此,我们只需要把负的原码整数转换为补码表示的负数,即可把整个32位当做整数来进行比较。即,如下范围的十六进制数字:
[0x80000000, 0xffffffff]
被原码解释为 [-0, -(2^31-1)] ,注意左大右小;
而它们按补码被解释为 [-2^31, -1],是左小右大的正常顺序
我们只需要用一个合适的常数减去该补码解释,使之变为补码下的原区间[-0, -(2^31-1)]即可;而区间两端的数字相加,均得-2^31,所以该数即是0x80000000。