整数和浮点数在内存中存储

转载一个视频里关于红灯的介绍和分享:

我大步流星,在人生大道,
没有长亭依靠,没有驿站歇脚。
红灯远远地亮起,也远远未到,
这天地还可容我继续呼啸。
蝉在耳边鸣叫,
思绪爬上树梢,
眺望路口后不远的小山,不算太高,
可我神游半天都没上至山腰。
山的背后是波涛,
是云雾缭绕,
是红日漫天烧,
如果在红灯前焦躁,走了弯道,
这些美丽便再也见不到。
于是我停在十字路口,默默祷告:
不为红颜一笑,
不为攀得更高,
只为当绿灯亮起时,我还在年少。

前言

  推荐使用电脑端观看
  本文只是介绍和梳理一下整形和浮点数在内存中存储的知识,并不做更多的深入拓展,强调是介绍表面的东西。

整数在内存中存储

  整数的二进制表示方法有有三种,即原码、反码和补码
三种表示方法均有符号位数值位两部分,符号位都是用0来表示 “正” , 用1表示 “负” ,而数值位的最高位的一位被当做符号位,剩余的都是数值位。比如char 类型:
如图所示

正整数的原、反、补码都相同
负整数的三种表示方法各不相同
原码: 直接将数值按照正负数的形式翻译成二进制得到的就是原码。如:

-5 -> 1 0000101,符号位为1,余下的七个位来表示5,为000 0101

反码: 将原码的符号位不变,其他位一次按位取反就可以得到反码。如:

-5 原码为1 0 0 0 0 1 0 1
  反码为1 1 1 1 1 0 1 0,符号位不变,其他位按位取反

补码: 反码 +1 就得到补码。如:

-5 的反码为1 1 1 1 1 0 1 0
    +  0 0 0 0 0 0 0 1
   ——————————
 补码为  1 1 1 1 1 0 1 1

对于整形来说 :数据存放内存中其实存放的是补码。

补充个人理解(仅供参考):负数的存在在计算机存储中占据了内存的一半空间
比如 char 类型数据,有符号的 char 类型的范围是 -128 ~ 127 ,无符号的 char 类型的范围是0 ~ 255。而留给 char 类型的内存大小是 1个字节(即8个比特位),所以 char 类型能表示的最大整数值为255(二进制为1111 1111),也就是 char 类型只能表示出256 个数字(包括0)。既然给出了最高位为符号位,那么255就表示不出来了,能表示的正整数的最大值为127(即二进制为0111 1111)。如下图:
图片1
那么负数改变了这个圆,使它变成了这样,并解释我的看法,如下:
图3

浮点数在内存中存储

先请大家猜一下这段代码运行的结果是什么:

#include <stdio.h>
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;
}

结果是:
如图所示
带着困惑来到浮点数的存储中,首先我们介绍一下浮点数存储的规则:
根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的形式:

V = (-1)S x M x 2E

  • (-1)S表示符号位,当S=0,V为正数;当S=1,V为负数
  • M表示有效数字,M是大于等于1,小于2的
  • 2E是指数位

举例来说(请注意这一段引用):

十进制的5.0,写成二进制是101.0,相当于1.01 x 22
那么,按照上面V的格式,可以得出S = 0,M = 1.01,E = 2。
十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01 x 22
那么,按照上面V的格式,S = 1,M = 1.01,E = 2。

IEEE 754规定:
对于32位的浮点数,最高的1位存储 符号位(S),接着的8位存储 指数(E),剩下的23位存储 有效数字(M)。
对于64位的浮点数,最高的1位存储 符号位(S),接着的11位存储 指数(E),剩下的52位存储 有效数字(M)。
float类型浮点数内存存储:
float类型
double类型浮点数内存存储:
double类型
到了这里,我们再来说说浮点数存储的过程。首先我们要先知道,浮点数存储入内存的过程和它的二进制浮点数V的形式是不一样的,前者是存储时的情况;后者是当你看到一个浮点数时,你能想着它的二进制形式是怎样的。

浮点数存的过程

IEEE 754 对有效数字(M)和 指数(E),还有一些特别规定
有效数字(M)
前面说过,1 <= 有效数字(M)< 2 ,也就是说,有效数字(M)可以写成1.xxxxxx的形式,期中xxxxxx表示小数部分。

解释:
这个过程就像是科学计数法里的指数一样,例如
12345(十进制数),对于着科学计数法是 1.2345 ❌ 104
5.5(十进制数),对应二进制位101.1,科学计数法是1.011 ❌ 22(二进制表示),
                 V = (-1)0 ❌ 1.011 ❌ 22

IEEE 754 规定,在计算机内部保存有效数字(M)时,默认这个数的第一位总之1(因为1<=M<2),因此这个1.xxxxxx的这个1可以被社区,只保存后面的xxxxxx部分。
比如保存1.011的时候,只保存011,等到读取浮点数的时候,再把第一位的 1 给它加回去(因为我们之前只保存了小数部分)。
这样做的目的是节省1位有效数字。以32位浮点数为例,留给M只有23个比特位,将第一位的1舍去以后,等于可以保存24位有效数字(因为我们知道读取时会补上第一位的1,就是23位存小数 + 小数点前的1)。
而且,有效数字的存储是从高位开始存储至低位,有效数字没有那么多的时候,用 0 来补充

例如 5.5(十进制数) 的是 V = (-1)0 ❌ 1.011 ❌ 22,那么有效数字就是1.011
舍去小数点前的1,那么存储进内存的是 011 00000 00000 00000 00000
             23个比特位

至于指数E,情况有些不同
首先,E为一个无符号整数(unsigned int)
这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2027。但是,我们知道,科学计数法中的 E 是可以出现负数的,为了让负数也能表示出来,所以IEEE 754 规定,存入内存时 E 的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的 E ,这个中间数是1023。比如,210的 E 是 10,所以保存成32位浮点数时,必须保存成 10 + 127 = 137 ,即1000 1001。

例如 : 9 (十进制)转化为二进制为:1001,科学计数法为:1.001 ❌ 23,V = (-1)0 ❌ 1.001 ❌ 22
E(加上中间值) = 2 + 127 = 129
在内存中存储为:0 10000001 00100000000000000000000
        S  E        M

浮点数取的过程

指数 E 从内存中取出还可以再分为三种情况:

E不全为0或不全为1(比较常见)

这个时候采用之前存浮点数的规则,进行反解就可以得到原值。即指数 (E) 的计算值减去123(或1023),得到真实值,再在有效数字(M)钱加上第一位的 1 。
比如: 5.5的二进制形式为101.1,由于规定正数部分必须为1,即将小数点右移2位,则为1.011❌22,其最终的价码为(E)为 2 + 127 = 129,表示为10000001。有效数字1.011去掉整数部分为011,补齐0到23位为01100000000000000000000,则其二进制表示形式为:

0  10000001  011 00000 00000 00000 00000

E 全为 0

这时,浮点数的指数(E)等于 1 - 127(或者 1 - 1023)即为真实值,有效数字(M)不再加上第一位的 1,而是还原为 0.xxxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。

例如:
0  00000000  001 00000 00000 00000 00000
按照E 全为0 的规则,指数(E)的真实值是 1 - 127 = -126。
V = (-1)0 ❌ 0.001 ❌ 2-126,还原一下就是0.000000……001,中间有好多个0.

E 全为 1

这时,如果有效数字(M)全为 0 ,表示±无穷大(正负取决于符号位(S));

例题讲解

例题讲解

首先是整数存储的 9 ,在内存中的表示为:
0000 0000 0000 0000 0000 0000 0000 1001
1字节   2字节   3字节   4字节
但是被理解为浮点数存储后,出现的形式为:
0  00000000  00000 00000 00000 00001 001
采用浮点数规则取出,指数(E)全为0,则真实值为:1-127= -126
则浮点数V = (-1)0 ❌ 0.00000000000000000001001 ❌ 2(-126),显然该浮点数时一个比较接近0的正数,用十进制小数表示就是0.000000。

当使用float类型指针改写数据后,按照浮点数存储的规则,9(十进制数)对于的二进制为1001.0,对于的 V = (-1)0 ❌ 1.0010 ❌ 23,指数(E)的价码为 3 + 127 = 130
那么在内存中就表示为:0  10000010  0010 00000 00000 00000 0000
浮点数存储好后,用 int 类型的方式读取内存出来得到的是:
十进制的1 091 567 616计算机

可以发现确实符合我们的输出。
符合

补充:大小端字节序和字节序判断

什么是大小端?

这个只需要有所了解就行,具体概念如下:

大端(存储)模式: 是指数据的低位字节内容保存在内存的高地址处,而数据的高位字节内容,保存在内存的低地址处。
如图:
如图所示

小端(存储)模式: 是指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容,保存在内存的高地址处。
如图:
如图所示

使用编程软件判断大小端模式

第一种是使用内存监视

大家可以简单的敲一下代码去检验一下
如图所示

第二种是使用联合体判断

创建联合体声明,因为联合体是共用同一块内存空间。所以int a 和 char ch 是同一块内存空间,如下:如图所示

union Test
{
	char ch;
	int a;
};

这样,给 a 变量赋值 0x11223344(这是16进制数表示),那么我们看这个低位数据是否存储在低地址区域,就只看 ch 变量是否等于 0x44,是的话就是小端模式如果是0x11,则是大端模式
从结果可以发现我的机器是小端模式。
如图所示

总结

  1. 浮点数分为单精度浮点数和双精度浮点数,前者空间为4字节,后者为8字节。更大的空间理论上可以让数据更加精准,而且还能增大数值的表示范围。
  2. 浮点数的存储格式是符号位+指数位+尾数位,符号位表示正负号,指数位表示指数值,尾数位表示有效数字
  3. 浮点数的存储方式也决定了部分数据不能完美地存储,会有精度损失,所以会存在舍入误差
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值