ACM于1990年宣布William M. Kahan在浮点数运算标准的制定上的贡献而得到图灵奖[1]。
--关于浮点数
极理想状况下,判断两个实数 a 和 b 相等,这里不妨将两个数分别定义为double型,直接使用 a == b 语句。众所周知,这样的代码是不能正常工做的,缘由是,1.0000001 和 1.0 这两个数在绝大多数的状况下,认为它们是相等的,因此常常能看到以下的 C 或者 C++ 代码 if(fabs(a-b)<=eps_0) ,通常地,eps_0设置为很小的数,好比1.0e-6 或者 1.0e-7,直观地来说,就是说当两个数之间的距离(称为绝对偏差)很小很小的时候,咱们就认为它们是相等的。
若是数字的数量级很大时,好比下面这两个数10000.0001和10000.000,显然使用上述的距离判断的结果二者不相等,不过,咱们来看它们之间的相对偏差为(1000.0001-10000.000) /10000.000=1.0e-9,那么这二者也应该认为是相等的,因而使用相对偏差的想法,将代码改写为 if(fabs(a-b)<=eps*fabs(a)) [2] 。 相对和绝对的区别,就像前段时间朋友圈普遍流传的关于赚钱这件事情,别人的一个小目标是别人的小目标,每一个人要根据本身的能力有本身的小目标(相对),若是把别人的小目标看成全部人的小目标(绝对),只是一句调侃而已,天然当不得真。
使用相对偏差能够回避掉数字数量级较大的问题,不过,它对于数量级较小的问题解决起来效果不佳,考虑一种极端状况,好比被比较的两个数字分别为 a=0.0 和 b=0.00000001,这个时候这两个数字相差很小,认为相等,可是使用相对偏差的代码时,因为右端项为零,而左端项大于零,显然等式不成立,即便用该方法判断两个数字并不相等。 比较完善的方式是将绝对偏差和相对偏差结合起来考虑,来设计C++代码:
bool isEqual(const double a, const double b) {
const eps_0 = 1.0e-6, rel_error = 10e-4;
bool isEqualFlag = FALSE;
if (fabs(a-b)<=eps_0) {
isEqualFlag = TRUE;
}
else {
if(fabs(a) >= 1000.0) {
if(fabs(a-b)<=rel_error*fabs(a)) {
isEqualFlag = TRUE;
}
}
}
return isEqualFlag;
}
这段函数貌似应该能够正常工做了,程序中的 rel_error 和 1000.0 能够根据实际须要进行修改,红色的标注部分是采用王珉老师的关于分状况讨论的方式,这样相较于不分状况讨论的方式来讲,可以有效减小计算量,有兴趣的同窗童鞋能够查看后面的回复。
注:读过 Kanan 的文章或者书,名字有印象,读的内容则彻底木有印象了 :-(
参考资料:
[1] http://baike.baidu.com/link?url=dKEdlcVXDrqZPOwlYrdGJHIUj2R2o6FTrytdnEKegTdp8pAobEAgln8xafbg9aVeGrmX7ZZDzfH4EmpnmOLCYzDnlvuhooaZwN1Hr1V53gkTmvLeTCuHbZrd4DAOaYoMHoWy35OqHJ_iW42A1fT61q
[2] http://baike.baidu.com/link?url=zliuoxROiLTdcJ7gdz0smcHam_4wEUeOpbFJG_mvS7aicaZvHt37-w0GYSi83ApuCAAGi-O8-7NrStNdPMciN4ztQFvyYBlHrjVv8YIgZmEoK2gllb-3cjV9j9cSEC7E
[3] http://www.cnblogs.com/zourrou/archive/2011/05/08/2040712.htmlhtml