【浮点型的存储】整型:凭什么你比我特殊?

简介: 我们在前一章数据的存储中介绍了除了浮点数类型以外其他类型在内存中的存储,本章将给大家分享浮点数在内存中的存储的详解,希望我的文章能帮到大家.

1. 前言🚩


我们在前一章数据的存储中介绍了除了浮点数类型以外其他类型在内存中的存储,本章将给大家分享浮点数在内存中的存储的详解,希望我的文章能帮到大家.


2. 浮点数存储与整型存储的关系🚩


如果想直接查看浮点数在内存中的存储比较难,这里我们举一个例子代码来看看


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,我们的第四个打印同理,以浮点型的形式打印浮点数应该是打印9.000000


b95731288bab4540a0c68b3625d44a2e.png

但是我们发现中间两个打印很不符合逻辑,一个打印的是0,还有一个打印了很大的数,也可能是打印的随机值,到这里我们可能已经知道了浮点数在内存中的存储规则和整型是完全不一致的,整型的存储补码在内存中,并且分为有符号位整型和无符号位整型,那么浮点数是怎样存储的?


3. 浮点数存储规则🚩


根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数 V 可以表示成下面的形式:


(-1)^S * M * 2^E

(-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数。

M表示有效数字,大于等于1,小于2。

2^E表示指数位。

这个规则是什么意思呢?我们举个例子,比如这里有一个浮点数为 5.5.我们把这个十进制的浮点数先转换为二进制的浮点数,为 101.1 1.然后二进制的101.1可以写成1.011×22 2.转换为这种形式后,我们就一一对应上面的S,M,E.


S=0(因为这个数大于0)

M=1.011

E=2

划重点! 相当于所有的浮点数都可以用S,M,E这三个数表示出来,所以我们的内存中只需要存储这三个数即可代表浮点数:



1060167d98dc4946bc55242dfe7e0710.png

然而对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。



25e80ad1bcb4491fb850e16d4eae6e00.png

这里我们就用32位机器来做讲解,我们知道float类型占四个字节.也就是32个bit位(也就是占32个二进制位),S只占一个bit位,因为S要么是1要么是0,它只用来表示浮点数的正负.E占八个bit位,它最大能存储的是255.M的话是占23个bit位.


4. IEEE 754特殊规则🚩


IEEE 754对有效数字M和指数E,还有一些特别规定:


前面说过, 1≤ M <2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。


这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。


至于指数E,情况就比较复杂


首先,E为一个无符号整数(unsigned int)

这意味着,如果E为8位,它的取值范围为0 ~ 255;如果E为11位,它的取值范围为0 ~ 2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。


懂了这些规则,我7e1dd94965e2428fadb1fb7f3b4fe57a.png们前面距离的5.5浮点数在内存中的存储就应该是这样的:




5. 从内存中取出E的情况🚩


指数E从内存中取出还可以再分成三种情况:


E不全为0或不全为1

这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。

比如:

0.5(1/2)的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为1.02(-1),其阶码为-1+127=126,表示为01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为:


0 01111110 00000000000000000000000

1

E全为0

这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。


E全为1

这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s)


6. 对前面代码的解释🚩


我们把前面的代码和截图拿过来:


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;
}




b95731288bab4540a0c68b3625d44a2e.png


我们首先来看第二个打印信息,n现在是以整数的形式存储在内存中,它在内存中的补码是

00000000 00000000 00000000 00001001

但是我们打印信息时,是要将它以浮点数的形式打印出来,所以我们要以浮点数的形式翻译这一段二进制代码,我将它分为浮点数的存储形式方便观察:

0 00000000 00000000000000000001001

我们可以发现,存储E的八个bit位全为0,当E全为0时,相当于它的值等于 (-1)S * M * 2-126,这个值是很小的,float类型的小数点后面六位都是发现不了有效数的,所以这里打印0.000000



接下来再来看第三个打印信息,这里我们把指针解引用后将浮点数9.000000存入内存,这里因为存储的是浮点数,所以在内存中以浮点数的存储规则来生成这32个bit位:(-1)0 * 1.001 * 23

0 10000010 00100000000000000000000

其中,存储E的八个bit位是3+127=130的二进制形式.当我们打印信息时,是按%d打印,也就是按照整型的形式打印,所以编译器在取出数据时是以取出整型的方式来翻译的,我把它写成规范式

01000001 00010000 00000000 00000000

我们再在程序员计算器算出这段二进制代码的十进制形式:



ae3f100d669c4eda9b43880484cf25be.png

和最后的结果保持了一致,我们的分析是正确的!


7. 总结🚩

浮点数在内存中的存储与整型的存储是有差异的,这一节的内容不需要大家全部背下来,你只要大致知道浮点数在内存中的存储是怎么回事就行了.看见类似于上面第一步的代码你也要能解释出来


🔎 C语言高阶 🔍

这里5.5转为二进制为什么是101.1?首先整数位的5转换为101是没有问题的,而0.5实际上是这样来的:二进制的0.1实际上是写成了1×2-1=0.5.就像101转换为10进制是写做1×22+0×20+1×20=5.这里的小数也按照这个规则来操作 ↩︎


我们知道十进制的1011可以转换成1.011×103,而我们这里的101.1是二进制数,转换成1.011×22,底数2代表这里是二进制数,指数2代表小数点向左移动了两位 ↩︎


相关文章
|
4月前
|
监控 安全 数据安全/隐私保护
55_大模型部署:从云端到边缘的全场景实践
随着大型语言模型(LLM)技术的飞速发展,从实验室走向产业化应用已成为必然趋势。2025年,大模型部署不再局限于传统的云端集中式架构,而是向云端-边缘协同的分布式部署模式演进。这种转变不仅解决了纯云端部署在延迟、隐私和成本方面的痛点,还为大模型在各行业的广泛应用开辟了新的可能性。本文将深入剖析大模型部署的核心技术、架构设计、工程实践及最新进展,为企业和开发者提供从云端到边缘的全场景部署指南。
|
Java Linux
【网络】高并发场景处理:线程池和IO多路复用
【网络】高并发场景处理:线程池和IO多路复用
423 2
|
存储 Oracle 关系型数据库
MySQL8 中文参考(二十九)(2)
MySQL8 中文参考(二十九)
477 9
|
JSON JavaScript 前端开发
Vue中的axios深度探索:从基础安装到高级功能应用的全面指南
在Vue项目中,高效的前后端通信是构建丰富用户体验的关键。axios作为前端与后端沟通的桥梁,其重要性不言而喻。本文将带您领略axios的魅力,从基本概念、安装方法,到高级应用技巧,助您快速掌握在Vue中利用axios进行HTTP请求的精髓。我们不仅会探讨axios的基础用法,如GET、POST请求,还将深入探索跨域配置、全局注册以及设置拦截器等高级功能,助您轻松实现优雅的前后端通信。
|
存储 Kubernetes 负载均衡
深入探讨Docker生态系统,Docker Compose vs. Docker Swarm vs. Kubernetes:深入比较
Kubernetes适用于大规模、复杂应用程序和多云部署,具有高度可定制的部署配置和广泛的生态系统。 在选择时,还可以考虑将它们组合使用,以满足不同环境和需求。无论选择哪个工具,容器编排都将成为现代应用程序开发和部署的不可或缺的一部分。
2106 0
|
运维 监控 Kubernetes
微服务:监控体系,容器监控
微服务:监控体系,容器监控
微服务:监控体系,容器监控
|
安全 jenkins 持续交付
Jenkins常用插件介绍之权限控制插件Role-based Authorization Strategy
除了搭建jenkins时默认安装的插件之外,有时候扩展功能,还需要安装一些其他的插件,下面为大家简单介绍一下Role-based Authorization Strategy插件。
Jenkins常用插件介绍之权限控制插件Role-based Authorization Strategy
成功解决:java: 无效的目标发行版: 17
这篇文章讲述了如何解决在启动SpringBoot项目时遇到的"无效的目标发行版: 17"的问题,主要是通过修改IDEA内置的编译设置,确保它使用正确的JDK版本。