浅谈浮点数的存储机制

在学习c语言的浮点数,知道了单精度float占4个字节32位,双精度double占8个字节64位,同时了解了一下float和double的二进制值具体表示时的位数分配,如下:

  • 单精度:
    第1位符号位,表示该数的正负,0位正,1为负。
    接下来的8位表示该浮点数的指数位。
    最后的23位表示尾数部分。

  • 双精度:
    第1位符号位,表示该数的正负,0位正,1为负。
    接下来的11位表示该浮点数的指数位。
    最后的52位表示尾数部分。

学习该知识点的时候参考了一下两篇文章:
关于float/double的取值范围和表示! - C语言
关于浮点数在内存中的表示移位存储难点的理解

大家可以好好看下这两篇文章,对于浮点数的很多地方会有清晰的了解。

我重点说一下其中难以理解的一些概念吧,说下我的个人理解,这两篇文章都没有很简明易懂的说清楚的概念。

  • 指数位:
    比如一个十进制数 8.25,换算成二进制的科学计数法是:1.00012e3*,具体如何换算可以参考上面的第一篇文章或者学习十进制与二进制的转换方法,这时候 指数 就是指 2e3 中的 3,而这个 3 就是存在浮点数的 指数位 中,根据上面第一篇文章,8.25转换存储后的真正的指数位的值如下:

    1000 0010
    

    大家可能会觉得有点奇怪,我们将这个二进制的值转换为十进制后应该是 130,并不是 3 啊,这是为什么呢?这就不得不说另一个比较难以理解的概念了,移位存储

  • 移位存储:
    大家知道,float的指数位的取值范围是 -127~128 之间的,为什么是这个范围呢,因为 指数位是8位,所有位都为1的话能表示 0~255 之间的范围,但是指数也是有正负的,故正负各占一半,即是 -127~128,怎么用这8个指数位表示这个范围呢?
    大家最容易想到的是,可以把这8位中的最高位指定为符号位,来表示正负,如下:

    0 0000000~ 0 1111111 即+ 0 到 127   (最高位0,表示正)
    1 0000000~ 1 1111111 即- 0 到 -127  (最高位1,表示负)
    

    大家会发现,这样多出了一个 +0-0,而且128没有二进制位来表示,说明加符号位的方式不可行,那怎么办呢?
    于是聪明的人发明了一种方法,将 0~255 之间的值与 -127~128之间的值一一对应上,不就可以了么,什么意思,举例说明下:

    00000000    最小值,正常表示十进制 0,现在表示十进制 -127
    01111111    正常表示十进制 127,现在表示十进制 0
    10000000    正常表示十进制128,现在表示十进制 1
    11111111    正常表示十进制255,现在表示十进制 128
    

    看到没,现在的正常的最小值0和最大值255对应到了我们需要的最小值-127和最大值128,中间值每一个都 是一一对应上的,且对应上的两个值之差都为 127,这样就成功的解决了指数位范围的问题。

    那我现在有一个任意的十进制指数值,比如 73,怎么找出他实际存储的指数位的值呢?
    解决方案是 给这个指数值先加上 127,再转换为8位二进制位!

    73 +127 = 200
    200转换为8位二进制位为:11001000
    

    11001000 就是 73 的指数位的存储值,这也就是所谓的移位存储,将一个十进制值在转化为二进制值的过程中,先将该十进制的值加 127,在进行转换为二进制。

    清楚了 指数位和移位存储的概念,对于浮点数的存储机制的理解就会顺畅多了。