浮点数精度对编程使用的影响

浮点数在计算机编程中一定会使用,然而使用中会有很多坑。最近做机械臂就被跳到坑里了。机械臂三自由度反解出来的cos值比1大一点点,1.00000...4。就是这一点点,坑了我一晚上,坑了师兄一上午。计算公式没问题,求解程序没问题,就是算出来的值有问题!后来才反应到是浮点数存储造成的。

 

举一个最简单的浮点数存储影响的例子。

 

double targetValue = 0.21;
double counter = 0;
int main()
{
    while(counter != targetValue)
    {
        counter += 0.01;
    }
    return 0;
}

上面的程序,按照我们想想的,while循环将执行21次,之后counter和targetValue相等,循环结束。

 

但是事实上,运行的时候并不是这样。这里我截取几个vs调试的变量值来说明。

 

首次进入循环前,可以看到targetValue的值并不是我们设置的0.21,而是0.20999...

 

在每次相加时,counter也不是加0.01,而是和0.01差距很小的一个数,经过20次相加之后并不是0.2,而有差距。

 

当相加21次时,两个数并不相等,所以无法退出循环。

 

可以看出,浮点数并不如我们认为的那么精确。实际上,造成这个的原因是因为计算机的存储机制造成的。计算机使用二进制存储数据,那么对于0.21这个数,下面是转换的过程,乘二取整法。

0.21*2=0.42 整数为0 -> 0.0

0.42*2=0.84 整数为0 -> 0.00

0.84*2=1.68 整数为1 -> 0.001

0.68*2=1.36 整数为1 -> 0.0011

0.36*2=0.72 整数为0 -> 0.00110

最后可以表示为:

0.0011010111000010…

可以看出,转换成二进制之后,是一个无限的数,不能精确的表示出来。所以就造成了这个bug。这也是我们程序中,cos算出来超过1一点点的原因。我在matlab里还原了c++的算法,matlab计算出的数值就是1,没有任何小数。

对于上面比较的问题,很多人都知道解决方法,那就是两个浮点数求差,差值很小就认为相等。程序如下:

double targetValue = 0.21;
double counter = 0;
int main()
{
    while(fabs(counter - targetValue) > 1e-5)
    {
        counter += 0.01;
    }
    return 0;
}

 

关于cos计算的bug,我最后采取的手段是将浮点数保留5位小数,精度能够符合我们的要求,而且不会被浮点数存储影响。

 

计算机上的坑真的多,争取毕业前多踩,多填坑。

posted @ 2018-02-06 17:35  huipengly  阅读(754)  评论(0编辑  收藏  举报