浮点数的误差是怎么形成的
在这里我用下面的简单的程序一步一步说明,程序很简单,应该很容易看懂。
#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(); }