float精度问题

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

一般情况下,float类型占32位,在内存中,从内存高位到内存低位,float表示为:
1位符号位 + 8位指数位 + 23位尾数。

比如float数32.75,其计算方法如下:
1) 先转化为二进制:100000.11;
2) 移动小数点,直到整数部分为1:小数点左移5位,1.0000011;
3) 尾数:去掉正数部分,留下小数部分,不够23位的后面补0,得到0000011 00000000 00000000
4) 符号位:0表示正数,1表示负数,这里为0;
5) 指数部分:左移为正,右移为负,这里是左移,所以指数值是5。

然后加上127(做这个处理的原因暂时还不是很明白,估计是要将负数转为正数),得到132,再转化为二进制10000100;
最后,拼接起来就是0 10000100 00000110000000000000000。

如果要转化为十六进制,是42030000,在X86的机子里面就表示为00 00 03 42。

比如float数0.0067,其计算方法如下:
1) 先转化为二进制:0.000000011011011100010111010110;
2) 移动小数点,直到整数部分为1:小数点右移8位,1.1011011100010111010110;
3) 尾数:去掉正数部分,留下小数部分,不够23位的后面补0,得到1011011 10001011 10101100
4) 符号位:0表示正数,1表示负数,这里为0;
5) 指数部分:左移为正,右移为负,这里是右移,所以指数值是-8。

然后加上127(做这个处理的原因暂时还不是很明白,估计是要将负数转为正数),得到119,再转化为二进制1110111;
最后,拼接起来就是0 1110111 10110111000101110101100。

如果要转化为十六进制,是3BDB8BAC,在X86的机子里面就表示为AC 8B DB 3B。

绝大部分的情况下,都有1位隐藏的高位“1”,但是很接近0的数就不会有,因为指数可以表示的范围是0 ~ 255,减去127,也就是-127 ~ 128,也就是最多右移127位,或者左移128位。

如果右移了这么127位后也得不到0,那么只能叹一句悲剧了,无可奈何。

然后再看一个例子:0.45,先转化为二进制:
0.01110011001100110011001100110011001100110011001100110011001100110011001100 11001100110011001100110011……,会发现根本无法将其完全转化为二进制(我计算到1000位也没有终止),类似于无限小数。

而尾数只有23位,所以精度问题就出来了。

0.45最终只能表示为0 01111101 11001100110011001100110,化为十六进制3EE66666,在X86的机子里面就表示为66 66 E6 3E。

再将3EE66666转化回来,得到0.44999998807907104,显然精度损失了。

所以说,float类型能表示的十进制的精度是log10(2^23) = 6.92…,如果算上隐含的高位,就是log10(2^24) = 7.22…,也就是书上通常说的6 ~ 7位的由来了。

由于存在精度问题,所以浮点数之间的比较有点特殊,不能按通常的做法进行。

比如:
所以两个浮点数之间的比较,以及浮点数和0之间的比较应该这么做:
其中FLT_EPSILON是参照VS的float.h中的定义的,其含义是“是1.0 + x != 1.0的最小正数”。

同样的,double类型、long double类型原理一样,也有相应的零界值:。

相关文档
最新文档