版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq1119278087/article/details/48851157
上篇中提到了ieee编码并留了一个浮点型数据存储与精度丢失的问题。本以为这是一个特别高深的底层问题,但经过查阅资料后发现,这个问题其实很简单,了解两个知识点就能清楚到底是怎么回事了。
- 知识点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……)
- 知识点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