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月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
37 3
|
9天前
|
存储 程序员 编译器
C 语言中的数据类型转换:连接不同数据世界的桥梁
C语言中的数据类型转换是程序设计中不可或缺的一部分,它如同连接不同数据世界的桥梁,使得不同类型的变量之间能够互相传递和转换,确保了程序的灵活性与兼容性。通过强制类型转换或自动类型转换,C语言允许开发者在保证数据完整性的前提下,实现复杂的数据处理逻辑。
|
12天前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
33 6
|
13天前
|
存储 数据建模 程序员
C 语言结构体 —— 数据封装的利器
C语言结构体是一种用户自定义的数据类型,用于将不同类型的数据组合在一起,形成一个整体。它支持数据封装,便于管理和传递复杂数据,是程序设计中的重要工具。
|
19天前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
80 12
|
11天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
30 1
|
16天前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
1月前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
46 3
|
2月前
|
编译器 程序员 C语言
深入C语言:动态内存管理魔法
深入C语言:动态内存管理魔法
|
2月前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
32 0