write in front
大家好,我是gugugu。希望你看完之后,能对你有所帮助,不足请指正!共同学习交流
本文由 gugugu 原创 CSDN首发 如需转载还请通知⚠
个人主页:gugugu—精品博客
欢迎各位→点赞 + 收藏⭐️ + 留言​
系列专栏:gugugu的精品博客
✉️我们并非登上我们所选择的舞台,演出并非我们所选择的剧本

前言

整数在内存中以二进制补码的形式存储,这个相信大家都很熟悉,那么浮点数又是怎么在内存中存储的呢?这篇博客将给大家详细讲解。

一、一道有意思的题目

#include int main(){int n = 9;float* pFloat = (float*)&n;printf("n的值为:%d\n", n);printf("*pFloat的值为:%f\n", *pFloat);*pFloat = 9.0;printf("num的值为:%d\n", n);printf("*pFloat的值为:%f\n", *pFloat);return 0;}

首先我们从这个题目引入

仔细去思考一下,这个题目将会输出什么?

答案还是比较奇怪的


为什么 会输出0.000000
又为什么会输出1091567616这么大的数字呢?

在学习完本章博客之后,就能够明白这道题目。

二、浮点数的存储

1、存储规则

根据国际标准IEEE(电气和电子工程协会) 754,任意⼀个⼆进制浮点数V可以表示成下面的形式:
V = (−1) S * M * 2E
• (−1)S 表⽰符号位,当S=0,V为正数;当S=1,V为负数
• M 表示有效数字,M是大于等于1,小于2的
• 2E 表示指数位

看着是不是很懵圈?

没关系,博主先举几个例子

首先先说5.5
5.5 = 101.101?

  • 是不是很多小伙伴会这么写? 哈哈,我猜对了吧,实际上这么写是错误的,
  • 因为2进制每一位都有权重 小数点后面第一位的权重是2-1 = 0.5
  • 所以 , 5.5=101.1
  • 又M大于等于1,小于2, 所以101.1=1.011 * 102” />2、在内存中的占位

    IEEE754标准还规定
    对于32位的浮点数(float),最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M

    如下图

    对于64位的浮点数(double),最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M

    如下图

    三、浮点数在内存中存储的具体实现

    1、对于S

    因为-1的0次方等于1,即正数,-1的1次方等于-1,即负数
    所以S中只用存储0或者1就可以完成符号的区分。

    2、对于M

    因为M大于等于1,小于2,所以M都是一点几,
    既然都相同,那就可以不用去存储了,直接去存储小数点后面的数字就可以了。

    这样做有什么好处呢???

    很显然,这样子做就可以省下储存1的那个比特,可以多储存一个二进制位。
    从而提高了浮点数的精度。

    3、对于E

    首先规定了E为无符号整形。
    因为可能出现负指数,所以IEEE754标准中提到一个词——中间数,
    指数加上这个中间数,就变成了非负整数。

    1. 对于32位,E的范围是0~255,中间数就是127
    2. 对于64位,E的范围是0~2047,中间数就是1023

    所以,前面提到的5.5,在内存中存储的就是
    S=0, E=2+127=129=10000001,M=011,因为未占满,所以后面全部补0,所以
    M=01100000000000000000000

    四、浮点数读取的过程的具体实现

    浮点数读取有三种情况

    a、第一种情况

    当E中既有0又有1时,这是,就是正常情况,根据存储的过程反向操作就行。

    b、第二种情况

    当E中全部都是0的时候,
    E的真实值会分成非常小,这个时候,这个浮点数也就是会非常非常小,非常接近于0。
    编译器就会将E=1-127或者1-1023,注意是1减,而不是0减。
    然后M不会在小数点前面重新加上1.了,而是直接变成0.xxxxxx
    通过这些操作来表示0或者一个十分接近0的浮点数。

    c、第三种情况

    当E中全部都是1的时候,
    这时,即使M中的数字为0时,也表示正无穷或者负无穷,即一个超级大或者超级小的数字。

    好啦,到这里,浮点数的存储规则讲解的差不多了,现在,我们可以来分析在最开始提出的那个题目了。

    五、解决题目

    #include int main(){int n = 9;float* pFloat = (float*)&n;printf("n的值为:%d\n", n);printf("*pFloat的值为:%f\n", *pFloat);*pFloat = 9.0;printf("num的值为:%d\n", n);printf("*pFloat的值为:%f\n", *pFloat);return 0;}
    • 首先在一个整形里面存储了一个9。
      注意这里是按照整形去存储的,所以,内存空间中存储的是(以小端机器16进制来写)09 00 00 00
      所以打印n 的时候是9
    • 接着将n的地址强制类型转换成一个浮点数指针,赋值给了pFloat
      先写成二进制吧
      0000 1001 0000 0000 0000 0000 0000 0000
      此时,按照浮点数的读取规则,E的真实值是18-127=-109,M=1.0
      所以pFloat=1.0* 2-109,非常非常小,且按照%f去打印,%f只打印6位小数,很显然,这个数的前六个小数位全部是0,所以,最终打印的是0.000000
    • 接着,通过指针指向n的空间,将内存存储的9以浮点数的形式改写成了9.0
      9.0以浮点数的存储方式,存储的是(-1)0 * 1.001 * 23
      所以S=0,E=3+127=130,M=00100000000000000000000
      所以32位二进制里面存储的应该是
      01000001000100000000000000000000
      以%d 十进制整形的方式读取,答案就是


    1091567616

    与答案相同

    • 最后以浮点数的方式打印9.000000

    所以四个答案都得到了很好的解释。



    okok,今天第二更奉上,关于浮点数的存储是C语言里面的一处比较重要的底层理解,希望大家能够学到不少东西吧。


    !!!!!!!!!!!!!!!!!求关注!!!!!!!!!!!!!!!!

    !!!!!!!!!!!!!!!蹲个一键三连!!!!!!!!!!!!!!!