数据存储之浮点存储


前言

浮点存储涉及到了非常多的计算机组成原理的相关知识,只要学好了这部分知识,相信肯定能和机组内容相串联,然后茅塞顿开。
本篇主要是用一个例题来讲解浮点数在内存中的存储规则,以及计算方法,希望能帮到大家。话不多说,直接上菜。


一、初看例题

#include<stdio.h>

int main()
{
	int n = 9;
	float* p = (float*)&n;
	printf("n=%d\n", n);
	printf("*p=%f\n", *p);
	*p = 9.0;
	printf("n=%d\n", n);
	printf("*p=%f\n", *p);
	return 0;
}

此题思考一下就能知道,第一个和第四个打印是能打印正确结果的,第二个和第三个会打印错误结果。
结果如下:
在这里插入图片描述
本篇重点并非只是分析题目对错,而是运用浮点数存储规则对其中的每个结果进行分析,以达到更加清晰地明白浮点数存储原理。


二、前提知识

1.浮点数的表示

什么是浮点数呢,小数点可以浮动的数就是浮点数(一般指二进制?)。也就是说,带小数点的就是浮点数。
例如:153.81665 0.6584 -5.000
这些数在内存中同样以二进制存储。例如 9.5 的二进制就是 1001.1 其中,权重从左往右逐渐变低,小数点后一位的权重是 2^(-1) 。
那浮点数的小数点前面和后面都有不知道多少位数,该怎么表示呢。我们规定让二进制的浮点数进行科学计数法。使得小数点前面只有1位,并且为1(不是0)。

其实,任意二进制浮点数都可以表示成 : V=(-1)S× M × 2E

S 是符号位,表示±得,M 是数据位 其组成为 1.xxxxxxxxxx ,E 是指数位,表示数的大小,具体要乘多少个2。

浮点数是有精度问题存在的。不是每一个数都可以被表示成二进制浮点数,可以无限接近,但却无法触及。
比如 9.6,它的二进制数是1001.1001100110011001100110011001,然后1001无限循环

2.浮点数在内存中的存储方式

知道了浮点数的表示方式,我们就可以在内存中表示他们了。只要表示S M E就能表示(可能有误差)。
由于M的小数点前面总是1,故在存储中可以将其抛弃,取出时带上1即可。所以M在内存中存储的是1.xxxxxx中的xxxxxx。

在32位系统中,S占1位,E占8位,M占23位。64位系统中,S占1位,E占11位,M占52位。

比如 9.5 ,32位中二级制组成为 1001.1 ,科学表示为(-1)0×1.0011×23 ,在内存中表示为:
0 10000010 00110000000000000000000 顺序是S E M。可能有人会疑惑,怎么跟我想的不一样啊?
接下来我们详细讲讲。

3.浮点数在内存中的存储方式的计算

我们都知道第一位的S是怎么来的,那E呢?其实,指数位E是个无符号类型,它根本无法表示负数,这跟我们的常理有悖,肯定是存在0.000几的浮点数的,无法表示应该怎么办。聪明的科学家的解决办法是,找到一个中间量,32位中E占8位,表示范围是0-255,中间量就是127。64位中E占11位,表示范围是0-2047,中间量就是1023。

我们用真实的E加上这个中间量(32位是127,64位是1023),就是我们存放在内存中的E的值。

比如说,真实E的值为2,那么2+中间量(这里考虑为32位)127得到的值就是存放在内存中的E,即129。用二进制表示就为 10000001 ,这就是内存中E的值,再看看上面的9.5,是不是就理解了。
因为M占的位数比较多,一般用不到这么多,所以不足位数的在后面补0即可。
再看上面的9.5,结果应该就明确了。

4.浮点数从内存中读取

我们知道了浮点数是如何存储在内存中的,接下来结束如何从内存中读取浮点数。
对于符号位和数据位,并无什么区别,怎么存入的就怎么读取即可。
对于指数位E,由于,真实的E是加上了一个中间量存入的,所以在读取指数位E的时候,需要对E进行减法操作,将E减去一个中间量即可得到真实的E,32位环境是-127,64位环境是-1023。但是读取出来的数有三种情况:
1.当内存中存的E不全为0或者不全为1(即有0有1)
此时直接将内存中的数减去中间量当作指数即可,如内存中的 10000001 - 01111111就是 00000010 即2。
2.当内存中的E全为0的时候
此时内存E为 00000000 ,说明指数(真实E)要么是+129(计算机不考虑这种情况),要么是-127,这种情况,计算机都是当作-127来看,由于此数非常非常接近于0,计算机为了突出非常接近0的关系,计算方法并非是 0 - 中间量,而是 1 - 中间量,那此时结果不是不对了吗,计算机则会进行修正,读取的M则不再是1.xxxxxxx,而是0.xxxxxxx,M抛去了预制好的1,计算机就会显示0.几而不是1.几。
3.当内存中的E全为1的时候
此时内存为11111111,11111111 - 01111111为 10000000,即真实E为128,说明指数非常非常大,倘若此时M中的数字全为0,则代表这个数是无穷大或者无穷小,由符号位判定。

5.浮点数存取例题

#include<stdio.h>

int main()
{
	float f = 5.5f;
	return 0;
}

此时对 f 取地址,&f里面储存的是什么呢?
我们先写出 f 的二进制数:101.1 ,科学计数法:(-1)0×1.011×22
转换成内存中数据:S=0,M=011,E=2+127=129=10000001
所以 f 在内存中的数据为:0 10000001 01100000000000000000000
换个写法:0100 0000 1011 0000 0000 0000 0000 0000
16进制:0x 40 b0 00 00
故内存中应该是 40 b0 00 00 这个数(实际还是二进制,只是编译器显示为16进制)。
看看结果:
在这里插入图片描述

由于大小端的缘故(此时为小端存取),小端存储数据的低字节存放在低地址处,高字节存放在高地址处。大端存储则相反。

0x 40 b0 00 00左边是高字节,右边是低字节,内存中每一排,左边低地址,右边高地址,所以存放格式就是00 00 b0 40,完全一致。


三、再看例题

#include<stdio.h>

int main()
{
	int n = 9;
	float* p = (float*)&n;
	printf("n=%d\n", n);
	printf("*p=%f\n", *p);
	*p = 9.0;
	printf("n=%d\n", n);
	printf("*p=%f\n", *p);
	return 0;
}

1.当n为整形9时

指针p内存放的是n的地址,但是被强制类型转换成 float 类型了,所以计算机在通过指针p来对n进行打印的时候,会以为里面存放的数是浮点类型的数,会按照浮点数的读取方式读取。
n=9内存存的是补码:00000000 00000000 00000000 00001001(正数的原码反码和补码均相同)
按整数打印无异议,即打印9。
按浮点数打印计算机会以为:0 00000000 00000000000000000001001
由刚刚的知识可知,E全为0,判断为非常接近0的数,但由于 float 类型的精度只有6-7位,后面的直接去掉,然后前面补0,结果应当是0.000000。

1.当n为浮点型9.0时

通过指针p,将储存的值变成浮点数9.0,然后按照浮点数类型打印无异议,结果就是9.000000。
此时n里面的内存为浮点类型,9的二进制数是1001,且为正数,科学计数法:(-1)0×1.001×23 ,3+127=130,即10000010
所以内存中的值为:0 10000010 00100000000000000000000
在被当作整数读取的时候,计算机会看成:0100 0001 0001 0000 0000 0000 0000 0000
开头的0会被认作是正数,然后其余的就是数据部分。
在这里插入图片描述
故会被计算机当成整数1091867616,然后打印出来。
让我们再看看结果:
在这里插入图片描述
完全符合结果。

总结

以上,关于数据存储的知识以及介绍完了,数据存储的相关知识涉及到非常多的机组知识,大家有时间的话,建议还是把计算机组成原理给用心看看,对了解计算机深层的原理有很大帮助。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值