浮点数的比较

来源:互联网 发布:模型 淘宝 编辑:程序博客网 时间:2024/04/28 08:00

一般来说,我们被教导不要去做浮点数的比较,比如用两个浮点数的比较来做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里面的floatdouble)。

基于这个原因,我们要避免浮点数的比较,除非你完全读懂了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可供浮点运算(比如嵌入式)。

可选择的方法就是采用非浮点高效算法(或者用整数数值算法来模拟浮点算法)。

 

原创粉丝点击