本文开始之前大家先看一道题目吧:
#include<stdio.h> int main() { double i; for(i=0;i!=10;i+=0.1) printf("%.1lf\n",i); return 0; } 运行结果是什么呢?不要凭感觉,亲自试一试就会发现这是无限循环的,为啥呢?这就是浮点数运算的陷阱!! 在IEEE标准中,浮点数是由符号位(1 bit),指数位(8 bits of float and 11 bits of double)以及精度位(23 bits of float and 52bits of double)组成的(计算机导论中学过)。 我们当时主要学习的是单精度浮点数,占用4个字节(32位)存储空间,包括符号位1位,阶码8位,尾数23位,假设符号位是sym,指数位是exp,精度位是pre,那么一个浮点数的值就等于(sym==1? - : +) 1·pre * 2^(exp-2^k)。其中,k=指数位的位数-1。例如1 10000001 11010010001000000000000表示一个float number,它的值是-1.11010010001000000000000*2^(129-2^7) = -1.11010010001000000000000*2。 但是,2进制的浮点数表示有一个很大的问题——它并不可以精确表示所有实数。即只有可以写成2^a+2^b+2^c+...这种形式并且精度不能太多的实数才可以用浮点数来精确表示。而大多数实数仅仅保存了一个四舍五入后的近似值而已。譬如,0.1在单精度浮点数中实际值为0.100000001490116119384765625。 正是这种非精确的表示形式,造成了浮点数运算的误差。不管加减乘除,只要涉及到了浮点数,你都得心里清楚:结果不是精确值,只是近似罢了。所以,在浮点数的运算中,请尽量避免用==比较结果,可以用 a+b<某个很小的数 来代替。 此外,0和无穷大在浮点数中是可以精确表示的,IEEE规定,所有位均为0的(包括符号位)表示0,除符号位全1表示正负无穷。 所以返回头来再看那道题,i+0.1永远加不到整型10,也就成了无限循环了。 由此又可以联想到7744问题(判断形如aabb的数字为完全平方数的一个问题): #include<stdio.h> #include<math.h> int main() { int a,b,n; double m; for(a=1;a<=9;a++) for(b=0;b<=9;b++) { n=a*1100+b*11; m=sqrt(n); if(floor(m+0.5)==m) printf("%d\n",n); } return 0; } 问题中为啥不直接比较floor(m)和m而是改为floor(m+0.5)呢?就是因为浮点数的运算可能存在误差—不是一定但是经常。 所以为了减少误差的影响,一般改为四舍五入的方式,即floor(x+0.5)。这可以用高中学习的思路把它想象成在数轴上把一个单位区间往左移动0.5个单位的距离。floor(x)等于1的区间为 ,而floor(x+0.5)等于1的区间为 。 暂时想了这么多,希望对大家有帮助,也希望大神们批评指正……!!!! |
笔记---浮点数运算的陷阱
最新推荐文章于 2022-03-11 21:47:08 发布