数据编码与精度问题(float)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq1119278087/article/details/48851157

上篇中提到了ieee编码并留了一个浮点型数据存储与精度丢失的问题。本以为这是一个特别高深的底层问题,但经过查阅资料后发现,这个问题其实很简单,了解两个知识点就能清楚到底是怎么回事了。

  1. 知识点1:
    首先让我们来复习一下整数转为二进制的过程
例子:十进制数9转为二进制
            商      余数
    9/2     4  ...  1  
    4/2     2  ...  0
    2/2     1  ...  0
    1/2     0  ...  1
    所以9的二进制码:1001

所有的整数按照这种方式转换,都能用一个准确的二进制表示,所以所有整数都能用二进制精确表示

然后我们再来看看浮点数转化为二进制的过程

例子:3.2转为二进制
整数部分同上3-->0011
小数部分0.2
    0.2 * 2 = 0.4   取整数部分  0
    0.4 * 2 = 0.8   取整数部分  0
    0.8 * 2 = 1.6   取整数部分  1
    0.6 * 2 = 1.2   取整数部分  1
    0.2 * 2 = 0.4   取整数部分  0
             .
             .
             .
    所以3.2的二进制码:11.00110011...(这不是最后的二进制码,这是为了方便理解进行整数与小数换算后的凑合)

可以看出,小数部分是不能用二进制准确表述出来的。这就像我们常用的十进制不能准确表示1/3 — 0.333333……
二进制也不能准确表示1/10 (0.1–>0.2–>0.4–>0.8–>(1.6取小数)0.6–>(1.2取小数)0.2……)

  1. 知识点2

float在c中,根据不同的环境,32位 — 4字节 64— 8字节(这个我以后在去写一个详细的) java中4字节
4字节有32位,分配情况如下:

4字节    31         30       29 --- 23    22 --- 0
     实数符号位   指数符号位   指数位      小数位

实数符号位   0 --                  1 -- 
指数符号位   0 -- 小数点右移         1 -- 小数点左移
//ps这里注意 右移说明指数为负,指数位在换算成二进制后要取反
  • 举例:3.2(左移)
    上面已经算过一次了,现在接着换算
11.0011001100110011001100110011…
小数点左移1位 1.10011001100110011001100110011…
截取小数点后23位 1.10011001100110011001100(就是因为这里的截取,导致精度的损失)
0 – 22位 10011001100110011001100
小数点左移–>30位 1
小数点左移1位–>22-29 0000000(补码)
3.2是正数–>31位 0

最后3.2在计算机中的存储形式就是:
01000000 01001100 11001100 11001101
末尾怎么就变成1了呢?原来是因为1100|1100 截断处后面如果是1,末尾会进位

  • 再举个例子 0.23(右移)
    再举个例子
例子:0.23转为二进制

    0.23 * 2 = 0.46   取整数部分  0
    0.46 * 2 = 0.92   取整数部分  0
    0.92 * 2 = 1.84   取整数部分  1
    0.84 * 2 = 1.68   取整数部分  1
    0.68 * 2 = 1.36   取整数部分  1
    0.36 * 2 = 0.72   取整数部分  0
    0.72 * 2 = 1.44   取整数部分  1
    0.44 * 2 = 0.88   取整数部分  0
    0.88 * 2 = 1.76   取整数部分  1
    0.76 * 2 = 1.52   取整数部分  1
    0.52 * 2 = 1.04   取整数部分  1
    0.04 * 2 = 0.08   取整数部分  0
    0.08 * 2 = 0.16   取整数部分  0
    0.16 * 2 = 0.32   取整数部分  0
    0.32 * 2 = 0.64   取整数部分  0
    0.64 * 2 = 1.28   取整数部分  1
    0.28 * 2 = 0.56   取整数部分  0
    0.56 * 2 = 1.12   取整数部分  1
    0.12 * 2 = 0.24   取整数部分  0
    0.24 * 2 = 0.48   取整数部分  0
    0.48 * 2 = 0.96   取整数部分  0
    0.96 * 2 = 1.92   取整数部分  1
    0.92 * 2 = 1.84   取整数部分  1
    0.84 * 2 = 1.68   取整数部分  1
    0.68 * 2 = 1.36   取整数部分  1
    0.36 * 2 = 0.72   取整数部分  0
    0.72 * 2 = 1.44   取整数部分  1
             .
             .
             .
    所以0.32的二进制码:0.001110101110000101000111101...
0.001110101110000101000111101…
小数点左移3位 1.110101110000101000111101…
截取小数点后23位 1.11010111000010100011111(精度损失,截取处后面是1,结果进位)
0 – 22位 11010111000010100011111
小数点右移–>30位 0
小数点右移3位–>22-29 得到0000011,各位取反,得到1111100(补码)
3.2是正数–>31位 0

所以最终0.23在计算机中的存储形式就是:
00111110 01101011 10000101 00011111

猜你喜欢

转载自blog.csdn.net/qq1119278087/article/details/48851157