苦于浮点数的折磨,写个帖子总结下各种情况的浮点数进位问题。
一、正常输出时进位情况
1、float类型变量
float a;
scanf("%f,&a");
printf("%.xf",a);//x为整数,即小数位数
当输入a的小数位数小于x,a大小不变,精度为x
当输入a的小数位数大于x,若为x小于6,从x+1位四舍五入,x等于6,从x+n(n>1)位四舍五入(x小于等于6)。注:当x大于6时,由于浮点数存储的误差,对输出值的影响不能确定!
eg:1.000-->1.000000(x为6)
1.1236-->1.124(x为3)
1.12346-->1.123(x为3)
1.11111149-->1.111112(x为6)
2、double类型变量
同float
本质上输出时的进位情况是由浮点数在计算机中存储值的大小决定的
eg.0.3的二进制为0.0100110011001100110011001100110011001100110011001101
再转换成十进制为0.300000011920928955078125
这样就产生了精度误差
二、强制类型转换
double f = 3.456;
int i = (int)(f * 100)+ 0.5) / 100;
使用强制转换直接把小数部分切割掉
但有时可能会出现存储时的不精确进而影响类型转换时值。
eg:9.99999999999在储存中为10.000000001,这时强制转换就会变成10,产生四舍五入的错觉。
三、输出时的隐式强制类型转换
比如想下面这样的代码
double a;
scanf("%lf",&a);
printf("%d",a);
这样要出大问题,比如输入1.1,输出却是-1717986918,非常的amazing。
这是因为整型是以二进制补码的形式存储的,可以精确表示,存储的就是数值。但是浮点数不一样,浮点数是以编码的形式存储的,不是直接存储数值,而且有精度问题,浮点数是以“符号位+阶码+尾数”的形式存储。float类型符号位占1位,阶码占8位,尾数占23位,总共32位;double类型符号位占1位,阶码占11位,尾数占52位。所以说,如果printf在输出时,因为格式字符是“%d”,所以它会以整型的方式去解析浮点数的编码。将浮点数编码直接当做二进制补码来计算数值,就会出现上面的结果。
来量化分析一手:比如浮点数的编码为0 000 1000 0000 000 0011 0011 0011 0011 0011 0011 1111 0011 0111 0011 0001 0011 0,这里的第1个0表示符号,在这里是正数,接下来的11位表示阶码,这里表示130(3+127)。后面的52位表示尾数,而从int类型的视角来看,都是2的次方位。此外,由于int和double的大小不同,8字节的double会被4字节的int阶段,进而错上加错。
所以安全的方法是加上显式强制类型转换。
未完待续。。。。。。