大家好,这里是小编的博客频道
小编的博客:就爱学编程
很高兴在CSDN
这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!!
本文目录
那接下来就让我们开始遨游在知识的海洋!
浮点型数据在内存中的存储
引导
我们先来一个题目激一激,题目------读出下列代码运行的结果
代码:
c
#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)第一部分:存储位置
元素 | 存储位置 |
---|---|
float | 32bit位 |
S | 存放于第1个bit位 |
M | 存放于第2个bit位~第9个bit位,8个bit位 |
E | 存放于第10个bit位~第32个bit位,23个bit位 |
double | 64bit位 |
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 |
学完了这些,我们再回头看看引导的那道题
代码:
c
#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进行打印 |
这样我们就得到了