关于数据精度的一点总结

说到数据精度,不得不说的,就是数据长度。

这里聊了一下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个字节,可以存储更精确的数据,但数据精度仍然是数据存储时,不能回避的问题,所以在开发过程中,一定要选好正确的数据类型,以满足业务的需要。

posted @ 2019-12-20 14:52  rockmmm  阅读(1873)  评论(0编辑  收藏  举报