一般来说,我们被教导不要去做浮点数的比较,比如用两个浮点数的比较来做if或者while的条件。
第一个原因就是:
精度
浮点数在计算机内部是按照IEEE 754标准编码的,由于二进制的关系,0.1这样的数都不能被准确表示,只是一个近似值。
在上面IEEE 754的wiki页面,最后面有几个有趣的链接,你可以在线试试看,0.9的编码是怎样的,然后0.99……,不断的增加9的个数,看看编码有什么样的变化,到最后(大概是0.99999999999999999的时候),它的编码和1.0的没什么不同,一模一样(不论是32位还是64位的,即所谓的single precision和double precision,或者C里面的float和double)。
基于这个原因,我们要避免浮点数的比较,除非你完全读懂了IEEE 754,知道你在做什么,并且在不同的情况下有什么样的后果。
一个惯用法:
# define EPSILON 0.000001
double d1, d2, delta;
delta = d1 - d2;
if (delta < EPSILON) // 可以认为d1和d2相等
; // do something
如果确实需要高精度的计算,考虑Google(高精度库)之类的。
除了精度问题,其实还有一个原因:
浮点数的运算
浮点数在计算机内部是怎样计算的(比较的)?让我们来看看(这里用的是x86,Windows 7,VS2010,嗯,有点夸张,你可以选择任何合适的平台):
double d1 = 1.0;
002B17BE fld1
002B17C0 fstp qword ptr [d1]
double d2 = 2.0;
002B17C3 fld qword ptr [__real@4000000000000000 (2B5830h)]
002B17C9 fstp qword ptr [d2]
double d;
if (d1 < d2)
002B17CC fld qword ptr [d2]
002B17CF fcomp qword ptr [d1]
002B17D2 fnstsw ax
002B17D4 test ah,41h
002B17D7 jne main+44h (2B17E4h)
d = d2 - d1;
002B17D9 fld qword ptr [d2]
002B17DC fsub qword ptr [d1]
002B17DF fstp qword ptr [d]
else
002B17E2 jmp main+4Dh (2B17EDh)
d = d1 - d2;
002B17E4 fld qword ptr [d1]
002B17E7 fsub qword ptr [d2]
002B17EA fstp qword ptr [d]
return 0;
002B17ED xor eax,eax
基本上就是,浮点数的运算,大都要用到Fxx的语句(浮点指令),这些指令或多或少要耗时(相对一般指令来说);而且运算的结果还不能直接用来控制程序的执行(见蓝色的部分,需要将结果置入一般寄存器,然后在加上附加的判断),这又是一个损耗,这里是if还好,如果是个while,积少成多,会是不小的开销。
基于浮点运算的特性,我们要避免做浮点运算/比较,这里会有时间上的开销(对于时间有要求的任务),或者压根就没有FPU可供浮点运算(比如嵌入式)。
可选择的方法就是采用非浮点高效算法(或者用整数数值算法来模拟浮点算法)。