关于数据精度的一点总结
说到数据精度,不得不说的,就是数据长度。
这里聊了一下float这个类型,float占4个字节,共4*8=32位。下图是float在内存格式:
31:标志位,0表示正数,1表示负数。30-23:指数位,共8位。22-0:小数位,共23位。
示例:12345 = 0011 0000 0011 1001 = 1.1 0000 0011 1001 * 2^13
13为指数,存放到指数位。指数可正可负,所以这8位需要表示-127至128。本例中13保存为13+127 = 140 = 1000 1100。
1.1 0000 0011 1001存放在小数位,由于小数点前永远都是1,所以可以省略,只存1 0000 0011 1001,不足23位,后面补0,即保存为1 0000 0011 1001 0000 0000 00。
最终保存结果:0 1000 1100 1 0000 0011 1001 0000 0000 00
回过头来,再看12345字个数,二进制为1.1 0000 0011 1001 * 2^13,如果小数部份长度超过23位,就无法完整保存了,只能舍弃多余的部份。
比如1.1 0000 0011 1001 0000 0000 001*2^24,蓝色长度为23,实际保存时,只有蓝色部份会保存。
那么这个数,就与1.1 0000 0011 1001 0000 0000 000*2^24在内存中是一样的,但我们知道,这两个数肯定是不一样的,精度丢失!
按上述结论,float在整数范围内,精度能完全保存的最大值是1.1111 1111 1111 1111 1111 111*2^23,即1111 1111 1111 1111 1111 1111 = 16777215。
这个数+1,就成了1 0000 0000 0000 0000 0000 0000 = 1.0000 0000 0000 0000 0000 0000*2^24 = 167772156。
再+1,就成了1 0000 0000 0000 0000 0000 0001 = 1.0000 0000 0000 0000 0000 0001*2^24 = 167772157。
由于小数长度有23位这个限制,上述的两个数,红色部份都不会保存,即167772156和167772157在内存中保存是一样的: 167772156 = 167772157。
验证:
int main(int argc, char *argv[]) { float j,k; j = 16777216; k = 16777217; cout << j << endl; cout << k << endl; printf("%f\n",j); printf("%f\n",k); cout << (j=k?1:0) << endl; return 0; }
输出:
1.67772e+007 1.67772e+007 16777216.000000 16777216.000000 1
如果不考虑精度的问题,float能保存的最大值是1.1111 1111 1111 1111 1111 111(1 1111 1111 …… 1111 共128-23=105个1)*2^128 = 3.4028236692093846346337460743177e+38。
由于红色部份不会保存,因此这105位可以是1和0的任意组合,但他们最终都保存为1.1111 1111 1111 1111 1111 111*2^128,仅当这105位都为0,才没有丢失精度,即有2^105-1个数都丢失精度。
float如此,double也一样,只不过double长度为8个字节,可以存储更精确的数据,但数据精度仍然是数据存储时,不能回避的问题,所以在开发过程中,一定要选好正确的数据类型,以满足业务的需要。