最近在学习C语言,是从《C Primer Plus》开始的。看到数据类型这一章,对于float类型的舍入有些疑问。
首先,先贴出书中关于float储值的一个描述。
C标准规定,float类型必须至少能表示6位有效数字,且取值范围至少是10的-37次方-10的正37次方
。前一项规定了指float类型至少精确表示小数点后6位有效数字,如33.333333.后一项.......
#include <stdio.h>
int main(void)
{
float a,b,c;
c = 2.0e20;
b = 2.0e20 + 1.0;
a = b - 2.0e20;
printf("%f %f %e\n", a, c, c);
return 0;
}
4008175468544.000000 200000004008175468544.000000 2.000000e+20
书中关于得出这个奇怪的结果是这样描述的:
计算机缺少足够的小数为来完成正确的运算。2.0e20是2后面的有20个0.如果把该数+1,那么发生变化的是第21位。要正确运算,程序至少要储存21位数字。而float类型的数字通常只能储存按照指数比例缩小或者放大6或者7位有效数字。
关于这段解释,我是一脸懵逼。
- 2e20远小于float最小表示数的范围,+1怎么就不够了?
- float正数小数分开储存,32位,8位表示指数,24位表示小数,这样+1也不会超过。
是我遗漏了什么地方吗?这确实很困扰我。
简单说下吧,有时间再来补上。
以下结果基于
gcc5.4
如果把
1.0
换成1e2, 1e3...1e13
,得到的结果都是一样的。精度问题
float
使用23位存尾数的,注意这23位存的是01串,是二进制的尾数。由于
2^23 = 8388608
(只是简单计算,去看IEEE754知道还隐含了一个1),所以存储十进制的小数小数点后精度也就6、7位。计算
浮点数的计算,见维基百科这里
可知,浮点数计算需要把指数统一然后计算,那么问题来了,无论是
1.0
统一到2.0e20
还是2,0e20
统一到1.0
,尾数的精度都超过了上面的精度,所以就有了这种奇怪的结果。这个奇怪结果的产生和类型也有关系,
1.0
和2.0e20
都是double型,这意味着b = 2.0e20 + 1.0
是先对两个double型字面量计算,然后再赋给float型变量b
,精度丢失。题主有兴趣的话,去看看IEEE754标准,以及Google一下浮点数的计算