高效的浮点数相等判定

显然,两个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。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值