C语言——带你深度剖析数据在内存中的存储(下)

简介: C语言——带你深度剖析数据在内存中的存储(下)

次我们讲到了整数在内存中的存储,那么是否大家有没有跟我有同样的疑问————浮点数在内存中的存储是否跟整数是一样的存储方式呢??


话不多说,我们直接看下面代码

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  9  9.000000一样呢

我们往下看:

  • 我们思考一下,当我们以整数的形式放进去,以整数的形式拿出来就得到了我们的第一个答案9,但当我们以整数的形式放进去,以浮点数的形式拿出来的时候却不是我们想要的答案,这是不是说明在内存中以浮点数的形式取出来和以整数的形式取出来是不一样的,内存中整数的存储和浮点数的存储是有差异的。
  • 我们也要理解这个答案是如何产生的,这就要我们去搞懂浮点数在计算机内部的表示方法。
  • 根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以表示为:
  • (-1)^S*M*2^E
  • (-1)^S表示符号位     当s=0, V为整数, 当s=1,V为负数
  • M表示有效数字,大于等于1,小于2
  • 2^E表示质数位
  • 我们举个例子:十进制的5.0,写成二进制是101.0 ,相当于1.01x2^2。那么,按照上面V的格式,可以得出s=0,M=1.01,E=2。
  • 十进制的-5.0,二进制是-101.0,相当于-1.01x2^2。那么,s=1,E=2,M=1.01


IEEE 754对有效数字M和E,有特别规定的。当1<=M<=2时,M可以表示为1.xxxxxxxx的形式,其中xxxxxxxx表示小数部分。

IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存xxxxxxxx的部分。比如在保存1.01的时候,只保存了01,等到读取的时候,再把1加上去。


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


对了E,首先E是一个无符号整数,这意味着,如果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,即10001001。


  • E从内存中取出还可以再分成三种情况:
  • 第一种情况:E不全为0和1
  • 这时,浮点数就采用了下面的规则,即E减去127得到真实值,再将有效数字M前面的1加上去。比如:0.5(1/2)的二进制形式为0.1,由于规定整数部分必须为1,即将小数点后移1位,则为1.0*2(-1),按照规则-1要加上127得到126,表示为01111110,而尾数1,0去掉整数部分0,补齐0到23位00000000 00000000 0000000
  • 所以最终的二进制就表示为: 0 01111110 00000000 00000000 0000000
  • 第二种情况:E全为1
  • 这时,如果有效数字M全为0,表示+-无穷大(正负取决于S)。
  • 第三种情况:E全为0
  • 这时,浮点数的指数E等于1-127(或1023),得到真实值,有效数字M不再是加上1,而是还原成0.xxxxxxx的小数形式。这样做是为了表示正负0,以及接近0的数字。
  • 当然我们也要知道为什么不再加上1,真实值应该为-127,浮点数就可以表示为(-1)^S*2^(-127)*1.xxxxxxxx,这个数接近于0的,这时我们就有了这个规定,有效数字M前也不再加上1了

了解玩IEEE 754的规则后,回到我们最开始的问题,为什么0x00000009,还原成浮点数变成了0.000000了呢???       首先将0x00000009拆分,得到第一位符号s=0,后面8位的指数E=00000000 ,最后23位的有效数字为000 0000 00000000 00001001

int main()
{
  int n = 9;
  //9->     0 0000000 0000 0000 0000 0000 0000 1001
  //由于E为全0,所以符合第三种情况。
  //因此浮点数可以表示为V= (-1)^0*0.00000000000000000001001*2*(-126)=1.001*2^(-146)
  //很明显V是一个接近于0的数,所以结果表示为0.000000
  float* pFloat = (float*)&n;
  printf("n的值为:%d\n", n);
  printf("*pFloat的值为:%f\n", *pFloat);
  *pFloat = 9.0;
  //9.0->   1001.0    可以表示为(-1)^0*1.0010*2^3  所以S=0,M=1.0010  E=3
  //我们还原成二进制就是 0 10000010 001 0000  0000 0000 0000 0000
  //这个二级制换成十进制就是  1091567616
  printf("num的值为:%d\n", n);
  printf("*pFloat的值为:%f\n", *pFloat);
  return 0;
}

最后我们用计算机验证一下:


最后:希望这篇文章让你对浮点数的存储有新的认识,我也会不定时更新,分享我的学习经历。

相关文章
|
2月前
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
621 0
|
2月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
122 26
|
2月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
234 15
|
2月前
|
存储 缓存 NoSQL
内存管理基础:数据结构的存储方式
数据结构在内存中的存储方式主要包括连续存储、链式存储、索引存储和散列存储。连续存储如数组,数据元素按顺序连续存放,访问速度快但扩展性差;链式存储如链表,通过指针连接分散的节点,便于插入删除但访问效率低;索引存储通过索引表提高查找效率,常用于数据库系统;散列存储如哈希表,通过哈希函数实现快速存取,但需处理冲突。不同场景下应根据访问模式、数据规模和操作频率选择合适的存储结构,甚至结合多种方式以达到最优性能。掌握这些存储机制是构建高效程序和理解高级数据结构的基础。
198 1
|
2月前
|
存储 弹性计算 固态存储
阿里云服务器配置费用整理,支持一万人CPU内存、公网带宽和存储IO性能全解析
要支撑1万人在线流量,需选择阿里云企业级ECS服务器,如通用型g系列、高主频型hf系列或通用算力型u1实例,配置如16核64G及以上,搭配高带宽与SSD/ESSD云盘,费用约数千元每月。
186 0
|
9月前
|
存储 程序员 编译器
C 语言中的数据类型转换:连接不同数据世界的桥梁
C语言中的数据类型转换是程序设计中不可或缺的一部分,它如同连接不同数据世界的桥梁,使得不同类型的变量之间能够互相传递和转换,确保了程序的灵活性与兼容性。通过强制类型转换或自动类型转换,C语言允许开发者在保证数据完整性的前提下,实现复杂的数据处理逻辑。
|
8月前
|
消息中间件 存储 缓存
kafka 的数据是放在磁盘上还是内存上,为什么速度会快?
Kafka的数据存储机制通过将数据同时写入磁盘和内存,确保高吞吐量与持久性。其日志文件按主题和分区组织,使用预写日志(WAL)保证数据持久性,并借助操作系统的页缓存加速读取。Kafka采用顺序I/O、零拷贝技术和批量处理优化性能,支持分区分段以实现并行处理。示例代码展示了如何使用KafkaProducer发送消息。
|
9月前
|
存储 编译器 程序员
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
在C语言中,内存布局是程序运行时非常重要的概念。内存布局直接影响程序的性能、稳定性和安全性。理解C程序的内存布局,有助于编写更高效和可靠的代码。本文将详细介绍C程序的内存布局,包括代码段、数据段、堆、栈等部分,并提供相关的示例和应用。
293 5
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
|
9月前
|
存储 数据管理 C语言
C 语言中的文件操作:数据持久化的关键桥梁
C语言中的文件操作是实现数据持久化的重要手段,通过 fopen、fclose、fread、fwrite 等函数,可以实现对文件的创建、读写和关闭,构建程序与外部数据存储之间的桥梁。