我们先看一个代码。
在我刚开始遇到这个代码的时候我认为的输出值应该全为9但是运行结果却是
如图所示
那为什么会出现这种情况呢?我们一个一个的分析。
我们就要去了解一下关于计算机是怎么储存float和double类型的数据了。根据国际标准IEEE754任意一个二进制浮点数v都可以表示成下面的形式
(-1)^S*M*2^E
(-1)^S表示符号位,当S=0,v为正数;当S=1,V为负数。
M表示有效数字,大于等于1小于2.、
2^E表示指数位。
以1.5为例我们将其转化为2进制,在.以前就是1在小数点以后也是1因为小数点以后每一位的权重从开始到最后分别是2^-1 2^-2由此类推.所以1.5二进制就是1.1。可以写成(-1)^0 * 1.1 *2^0.
但是这种储存方式就会导致一些浮点数例如5.3这个数就绝对不可能准确的被保存于计算机中。我们将5.3转为2进制小数点之前就是101就把5表示出来了但是0.3却难以表示出来,首先小数点后一位我们只能填0因为第一位权重为0.5大于0.3,那就第二位为1为0.25那对于0.3我们就还差0.05但第三位又不是0.05只能继续往下。所以部分浮点数在计算机内部是难以和原值保持一致的。
所以计算机对于一个浮点数来说储存浮点数的时候计算机储存的就是S和M和E。
然后对于计算机来说float类型的数据占四个字节。32个比特位。
计算机就把首位用来储存S之后的八个比特位用来储存E接下来的23个比特位用来储存M。
对于double类型的数据首位依旧用来储存S之后的11个比特位储存E最后的52个比特位储存M。
下面就是单精度浮点数的内存储存模型
然后对于M和E他们储存的方式也不同。首先对于电脑来说E为一个无符号数它的取值范围为0到255
但是在实际情况中有时候确实会存在E是负数的情况;例如0.5转为2进制为0.1可以写成1*2^-1此时E就为-1.所以按照规定float类型E我们需要加上中间数127而double类型我们需要加上1023然后再将这个数改为2进制存入到那八个比特位中。
对于M根据IEEE754的规定在计算机内部保存M的时候默认这个数的第一位总是1因此可以被舍去只保存后面的部分例如1.01计算机就只会保存01即小数点后面的位数我们以5.5f作为例子首先改写成2进制就是101.1改写成IEEE754规定的就是(-1)^0 * 1.011 *2^2
S为0 E为2但是要加上中间值那么E就是129最后M就是01
在计算机内存中就是0//这是S 10000001//这是E,最后的23位前两位就保存01然后再以0补缺失的位
所以M就是01000000000000000000000将这三个保存起来就是5.5f在电脑中的储存形式
01000000101100000000000000000000
转成16进制40b00000
然后我们用代码来验证
我们通过调试的内存看a图下
现在我们就可以去解释上面的那个代码了。
取出这个值的时候由于我们遇到的E情况不同取出的规则也是不同的首先如果E为全零此时浮点数的指数就是-127那么取出时
有效数字M不再加第一位的1而是还原成0.xxxxxx的小数这样是为了表示+-0,以及接近于0的很小的数字。
E为全1时有效数字M全为0表示无穷大
以上两种都是特殊情况一般E既不是全0也不是全1那取出规则就是E的计算值减去127(或是1023)
得到真实值。最后有效数字M前加上第一位的1.
下面就是这个代码的运行打印情况
如果有错误欢迎各位指出。