Bootstrap

重生之我在异世界学编程之C语言:数据在内存中的存储篇(下)

大家好,这里是小编的博客频道
小编的博客:就爱学编程

很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!!

在这里插入图片描述


那接下来就让我们开始遨游在知识的海洋!

浮点型数据在内存中的存储


引导

我们先来一个题目激一激,题目——读出下列代码运行的结果

代码:

#include<stdio.h>
int main() {
	int n = 9;
	float* p = (float*) &n;

	printf("%d\n", n);
	printf("%f\n", *p);

	*p = 9.0;

	printf("%f\n", *p);
	printf("%d\n", n);
	return 0;
}

宝子们快用你们的小脑袋想一想吧!!!



















运行结果:
在这里插入图片描述

  • 在学习浮点型在内存中的存储之前,你是否只能准确预见第一行和第三行的输出情况?别担心,跟紧小编的步伐,来看看浮点型在内存中的存储

正文

1.十进制浮点数转换为标准存储的二进制浮点数


(1)第一步:十进制浮点数转换为二进制浮点数
部分转换方法
整数部分同十进制整数转换为二进制整数一样
小数部分根据权重进行配凑,从小数点后第一位2^-1开始
本质都是根据权重的大小不同进行配凑

(2)第二步:二进制浮点数转换为标准储存的二进制浮点数

根据国际标准IEE(电气与电子工程协会)754,任意一个二进制浮点数可以表示成这样的形式:(-1)^S * M * (2)^E

元素含义
S表示符号位,为0则为正数,为1则为负数
M表示有效数字,大于等于1,小于2
E表示二进制下的指数位

例:

十进制浮点数
5.0
101.0
-1的0次方*1.01*2的2次方
S=0
-5.0
-101.0
-1的1次方*1.01*2的2次方
M=1.01
E=2
S=1

2.标准存储的二进制浮点数的存储规则

(1)第一部分:存储位置
元素存储位置
float32bit位
S存放于第1个bit位
M存放于第2个bit位~第9个bit位,8个bit位
E存放于第10个bit位~第32个bit位,23个bit位
double64bit位
S存放于第1个bit位
M存放于第2个bit位~第12个bit位,11个bit位
E存放于第个13bit位~第64个bit位,52个bit位

实际效果:

在这里插入图片描述

在这里插入图片描述

(2)第二部分:存储的方式
元素存储方式
S无特殊,正常存
M因为总是满足1<=M<2,所以我们进行存储的时候,就不用把1存入,这样就节省了一位bit位,可存的M的范围就更大了
E较复杂,分问题和解决方案两个模块进行讲解
名称内容
问题首先,E在内存中存储时是一个无符号整数,只有浮点数整体才是有符号的,但是我们知道在科学记数法中,E存在有负数的可能
解决方案所以我们规定:给每个实际的E值加127(对于double是1023)再存入bit位中

以float为例:

十进制数
5.0
在内存中的存储的二进制位
00100000000000000000000010000001
-5.0
10100000000000000000000010000001

通过以上的两个知识点,我们就能了解够浮点型在内存中是如何存储的,但存进去了,又该怎么读取呢?接下来就来介绍读取的方法

3.浮点数的读取规则

(1)第一部分:占位符
占位符作用
%f把一个数据的二进制位以单精度浮点数(float)的读取规则进行读取
%1f把一个数据的二进制位以双精度浮点数(double)的读取规则进行读取

(2)第二部分:读取规则
以E的值划分为三种情况读取规则
E不为全0或不为全1(1)先把标准存储的二进制浮点数的形式读出来:S:是0真实值符号位就为1(正数),是1真实值符号位就为-1(负数);E:先把对应区域的二进制转换成十进制数,再-127(double类型则-1023)。M:直接把对应区域的二进制转换成十进制数,再在前面加上1和小数点;(2)最后:根据十进制浮点数转换为标准存储的二进制浮点数逆推回去得到真实值(十进制浮点数)
E全为1无穷
E全为0无限趋近于0

学完了这些,我们再回头看看引导的那道题

代码:

#include<stdio.h>
int main() {
	int n = 9;
	float* p = (float*) &n;

	printf("%d\n", n);
	printf("%f\n", *p);

	*p = 9.0;

	printf("%f\n", *p);
	printf("%d\n", n);
	return 0;
}
序号分析
1 int n = 9;首先我们创建了一个整型变量n,并为它分配了4个字节的内存空间
2这4个字节,也就是32个bit位存放的二进制序列为:000000000000000000000000000001001(原码)000000000000000000000000000001001(补码)
3 float* p = (float*) &n然后我们创建了一个浮点型指针变量float* p
4 printf(“%d\n”, n);接着用了%d的占位符对整型变量n的二进制序列(补码)进行访问和打印:%d是将一个二进制数列(补码)当成有符号的整型变量,并转换为原码的十进制打印。类比到这里,就是把00000000000000000000000000001001(补码)按照有符号整型的变换规则变成00000000000000000000000000001001(原码),再转换(十进制)9进行打印
5 printf(“%f\n”, *p);再接着用了%f的占位符对整型变量n的二进制序列(补码)进行访问和打印:%f是将一个二进制数列(补码)当成单精度浮点数,并转换为原码的十进制打印。类比到这里,就是把00000000000000000000000000001001(补码)按照单精度浮点数的读取规则变成(-1)^0 * 1.00000000 * 2^(-118)(二进制浮点数的标准存储形式),再转换(十进制)0.0000000……进行打印
6 *p = 9.0;再然后使用了解引用操作符*,对n的地址中原存的二进制序列00000000000000000000000000001001(补码)以单精度存储规则进行了重新存储:先把我们要存的9.0(十进制浮点数)变成(-1)^0 * 1.001 * 2 ^ 2 (二进制浮点数的标准存储形式),然后再转换为00010000000000000000000000000010(补码)
7 printf(“%f\n”, *p);再接着用了%f的占位符对现在的n中存储的二进制(上述补码)进行访问和打印:%f是将一个二进制数列(补码)当成单精度浮点数,并转换为原码的十进制打印。类比到这里,就是把00010000000000000000000000000010(补码)按照单精度浮点数的读取规则变成(-1)^0 * 1.001 * 2 ^ 2 (二进制浮点数的标准存储形式),,再转换(十进制)9.00000000……进行打印
8 printf(“%d\n”, n);最后用了%d的占位符对现在的n中存储的二进制(上述补码)进行访问和打印:%d是将一个二进制数列(补码)当成有符号的整型变量,并转换为原码的十进制打印。类比到这里,就是把00010000000000000000000000000010(补码)按照有符号整型的变换规则变成00010000000000000000000000000010(原码),再转换(十进制)1096517616进行打印

这样我们就得到了
在这里插入图片描述


快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!

;