【C语言】浮点数在内存中的存储和读取——底层分析

🚀write in front🚀

🔎大家好,我是gugugu。希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎

🆔本文由 gugugu 原创 CSDN首发🐒 如需转载还请通知⚠

📝个人主页:gugugu---精品博客

🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​

📣系列专栏:gugugu的精品博客

✉️我们并非登上我们所选择的舞台,演出并非我们所选择的剧本📩

前言

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

一、一道有意思的题目

c 复制代码
#include <stdio.h>
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 * 2^E^
• (−1)^S^ 表⽰符号位,当S=0,V为正数;当S=1,V为负数
• M 表示有效数字,M是大于等于1,小于2的
• 2^E^ 表示指数位

看着是不是很懵圈?

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

首先先说5.5

5.5 = 101.101?

  • 是不是很多小伙伴会这么写? 哈哈,我猜对了吧,实际上这么写是错误的,
  • 因为2进制每一位都有权重 小数点后面第一位的权重是2^-1^ = 0.5
  • 所以 , 5.5=101.1
  • 又M大于等于1,小于2, 所以101.1=1.011 * 10^2^?
  • 又错了, 注意,这里是二进制浮点数,不是10进制
  • 所以 101.1=1.011 * 2^2^
  • S呢?S怎么处理
  • 因为5.5是整数,所以S=0

至此 5.5 = (-1)^0^ * 1.011 * 2^2^

所有的浮点数都可以这么写,各位小伙伴可以自己举几个例子试试吧,当然,别太复杂,否则自己会写吐的。

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,中间数就是128
  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时,也表示正无穷或者负无穷,即一个超级大或者超级小的数字。

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

五、解决题目

c 复制代码
#include <stdio.h>
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 * 2^3^
    所以S=0,E=3+127=130,M=00100000000000000000000
    所以32位二进制里面存储的应该是
    01000001000100000000000000000000
    以%d 十进制整形的方式读取,答案就是

1091567616

与答案相同

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

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



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


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

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

相关推荐
五味香4 分钟前
Linux学习,ip 命令
linux·服务器·c语言·开发语言·git·学习·tcp/ip
lb36363636361 小时前
整数储存形式(c基础)
c语言·开发语言
浪里个浪的10241 小时前
【C语言】从3x5矩阵计算前三行平均值并扩展到4x5矩阵
c语言·开发语言·矩阵
<但凡.1 小时前
编程之路,从0开始:知识补充篇
c语言·数据结构·算法
f狐0狸x2 小时前
【数据结构副本篇】顺序表 链表OJ
c语言·数据结构·算法·链表
CoderBob2 小时前
【EmbeddedGUI】脏矩阵设计说明
c语言·单片机
浪里个浪的10242 小时前
【C语言】计算3x3矩阵每行的最大值并存入第四列
c语言·开发语言·矩阵
敲敲敲-敲代码3 小时前
游戏设计:推箱子【easyx图形界面/c语言】
c语言·开发语言·游戏
simple_ssn3 小时前
【C语言刷力扣】1502.判断能否形成等差数列
c语言·算法·leetcode
ahadee3 小时前
蓝桥杯每日真题 - 第10天
c语言·vscode·算法·蓝桥杯