浮点数的误差是怎么形成的
在这里我用下面的简单的程序一步一步说明,程序很简单,应该很容易看懂。
#include <stdio.h>
// 浮点数的误差是怎么形成的
void main()
{
double d = 1099511627775.9998779296875;
printf("d = %.14f\n", d);
// 问题描述:下面的数输出时并不是在编码时所给的数,有误差,是怎么形成的呢?
float f = 1099511627775.9998779296875F;
printf("d = %.14f\n", f);
// 这是我的机子的情况
printf("sizeof char is %d\n", sizeof(char)); // 1
printf("sizeof int is %d\n", sizeof(int)); // 4
printf("sizeof long is %d\n", sizeof(long)); // 4
printf("sizeof float is %d\n", sizeof(float)); // 4
printf("sizeof double is %d\n", sizeof(double)); // 8
// 先理论学习一下
/*
参考自http://en.wikipedia.org/wiki/IEEE_754
IEEE 754-1985标准规定,float用32位二进制编码表示,其中
最高位31位为符号位,0表示正,1表示负
30位-23位,共8位,为阶码,通过移码的方式保存指数部分,即指数值加上偏移量127(01111111)
22位-0位,共23位,为尾数,存储的是规范化二进制数(1.b1b2...bn),其中整数位前导1不保存,只保存小数位b1b2...b23
*/
// 所以定义以下联合体
union
{
int i;
float f;
unsigned char ca[4]; // 高索引表示数的高位
}dt;
dt.i = 0x01020304;
printf("%x %x %x %x\n", dt.ca[0], dt.ca[1], dt.ca[2], dt.ca[3]);
// 这里可以看出,数组从小到大依次表示数位从小到大,所以后面从大到小输出,这个是大小端的问题,有兴趣的请谷歌
dt.f = -1;
printf("%x %x %x %x\n", dt.ca[3], dt.ca[2], dt.ca[1], dt.ca[0]);
/*
当浮点数为-1的时候,十六进制输出为 BF 80 0 0
转换为二进制数为 10111111 10000000 00000000 00000000
重新将比特位分组 1 01111111 00000000000000000000000
得
符号位1,表示负数
阶数01111111b,即127,减去127,得0
尾数全为0,表示1.0b
所以整体表示 -1*1.0*2^0,即-1
*/
dt.f = 33;
printf("%x %x %x %x\n", dt.ca[3], dt.ca[2], dt.ca[1], dt.ca[0]);
/*
当浮点数为33的时候,十六进制输出为 0x 42 4 0 0
转换为二进制数为 01000010 00000100 00000000 00000000 b
重新将比特位分组 0 10000100 00001000000000000000000 b
得
符号位0,表示正数
阶数10000100b,即132,减去127,得5
尾数为1.00001b
所以整体表示1.00001b左移5位,即100001b,即33
*/
dt.i = 1024;
printf("%x %x %x %x\n", dt.ca[3], dt.ca[2], dt.ca[1], dt.ca[0]);
printf("%f\n", dt.f);
printf("%E\n", dt.f);
/*
当整数为33的时候,十六进制输出为 0x 0 0 4 0
转换为二进制数为 00000000 00000000 00000100 00000000 b
重新将比特位分组 0 00000000 00000000000010000000000 b
得
符号位0,表示正数
阶数00000000b,即0,减去127,得-127
尾数为1.0000000000001b
所以整体表示1.0000000000001b右移127位,即0.(126个0)10000000000001b,这是个很小的数,科学表示法为1.434930E-042。
*/
// 真正探索啦
dt.f = 1099511627775.9998779296875F;
printf("%x %x %x %x\n", dt.ca[3], dt.ca[2], dt.ca[1], dt.ca[0]);
printf("%.30E\n", dt.f); // 原数位小于30位,所以这里的表示是精确的
/*
当浮点数为1099511627775.9998779296875的时候,十六进制输出为 0x 53 80 0 0
转换为二进制数为 01010011 10000000 00000000 00000000 b
重新将比特位分组 0 10100111 00000000000000000000000 b
得
符号位0,表示正数
阶数10100111b,即167,减去127,得40
尾数为1.0b
所以整体表示2的40次方,即1099511627776,跟输出1.099511627776000000000000000000+012是相符的。
所以这里的误差根源为:1,减少幂次到39,置所有尾数位为1,则数只能表示到(2-2^(-23))*2^39,为1099511562240,比原数小了65535.9998779296875。
这就是浮点数在表示大数时的误差,随着数越大,误差越大。
同理可得出double类型的数在表示时,虽然精度比float小,但随着数的增大,误差也会越来越明显。
*/
getchar();
}
浙公网安备 33010602011771号