整型和浮点型在内存中的存储

本文详细介绍了整型和浮点型在计算机内存中的存储方式,包括原码、反码、补码的概念以及浮点数遵循的IEEE 754标准。通过实例分析了整型在不同情况下的转换,并探讨了浮点数的指数和有效数字的存储规则,强调了不同类型之间的转换可能导致的输出差异。
摘要由CSDN通过智能技术生成

整型和浮点型在内存中的存储

整型的存储

整型家族的表示范围: limits.h

计算机中的整数有三种表示方法,即原码、反码和补码。

而在计算机内存中存储的是 补码

三种表示方法均有符号位数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位负整数的三种表示方法各不相同。

符号位就是二进制码的第一位。

原码

直接将二进制按照正负数的形式翻译成二进制就可以。

反码

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

补码

反码+1就得到补码。

表现方式

正整数的原码、反码和补码相同。

负整数就需要先转化为反码,再转化为补码。

1(int类型)

00000000000000000000000000000001 (原码)

00000000000000000000000000000001 (反码)

00000000000000000000000000000001 (补码)

正整数的原码、反码和补码相同

-1(int类型)

10000000000000000000000000000001 (原码)

11111111111111111111111111111110 (反码)

11111111111111111111111111111111 (补码)

负整数就需要先转化为反码,再转化为补码。

我们来看看这段代码

#include <stdio.h>
int main() {
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a = %d , b = %d , c = %d", a, b, c);
	return 0;
}

输出结果为:

image-20220104152309558

为什么我们的 c =255 ,而不是 -1 呢

首先 c 是一个无符号字符(char)类型,所以它的原码应该是 10000001,补码为 11111111,而我们用占位符%d打印时,会将我们的数据进行整型提升,而我们的cunsigned char(无符号的字符类型) ,所以我们进行整型提升时,高位应该补0,因此补码为00000000000000000000000011111111 , 换算为十进制就是255.

再看一题

#include <stdio.h>

int main() {
	char a = -128;
	printf("a = %u", a);//注意这里是 %u(无符号整型输出)
	return 0;
}

输出结果:

image-20220104154804649

解析:

我们要先求出它在的补码

a的原码为10000000000000000000000010000000,补码为11111111111111111111111110000000,而现在我们只取求出补码的后八位,因为 a 是char类型所以需要截断后八位.得到补码 10000000.

而我们要输出的是 %u,而我们的a又是char类型,所以高位补 1 ,得 11111111111111111111111110000000,而这个数换算为十进制就是 4294967168.

分析这段代码

#include <stdio.h>
#include <string.h>

int main() {
	char a[1000] = {0};
	for (int i = 0; i < 1000; i++) {
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));
	return 0;
}

输出结果:

image-20220104165520198

解析:

有符号的char类型的取值范围为-128~127,无符号的为:0~255

有符号char的取值范围的由来:

以下为二进制码

00000001 (1)

00000010 (2)

00000011 (3)

01111110 (126)

01111111 (127)

10000000 (-128)

10000001 (-127)

11111110 (-2)

11111111 (-1)

因为是char类型,只有八个比特位,如果这里再进 1, 就会变成100000000,所以需要抛弃越界的比特位.

00000000 (0)

00000001 (1)

由此可见,其实是一个循环

7797d00bc61b7f7bf1d959b93ce60aa

strlen求取的长度是以 \0 为标志的,而 \0 的本质就是 0 ;所以在找到0的时候就会停止,而这个数从-1开始,正好有255个字节.

浮点型的存储

浮点数的表示范围: float.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; 
}

image-20220104175419034

大家可以思考一下,为什么以上代码的输入结果为何如此.

其实也正好说明了浮点数和整数在内存中存储的方式是不同的,现在我们一起来学习float的存储方式吧.

浮点数存储规则

浮点数没有原码,反码,补码

根据国际标准IEEE(电气和电子工程协会)754规定,任意的一个二进制浮点数V,可以这样表示:

  • (-1)^S * M * 2^E
  • (-1)^S表示符号位,但S = 0,V为正数,当S=1,V为负数;
  • M表示有效数字, 1<M<2;
  • 2^E表示指数位

在32位的浮点数(float)中

最高位 也就是第一位是符号位 S ,接着的8位是指数E,剩下的23位为有效数字M

image-20220104180525538

对于64位的浮点数,最高的1位也是符号位S,接着的11位是指数E,剩下的52位为有效数字M

image-20220104180816588

IEEE 754对有效数字M和指数E,还有一些特别规定。

前面说过, 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。

IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。

比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。

至于指数 E,情况就比较复杂。

首先,E为一个无符号整数(unsigned int)

这意味着,如果E为8位,取值范围是0~255, 如果E是11位,它的取值范围是0~2047,如果科学计数法中的E是负数的话,存入内存时的E就需要加上一个中间值,对于8位(float)的E来说,这个中间值是127,对于11位(double)的E,这个中间值是1023

E不全为0或不全为1

这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。

E全为0

这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,但这个值已经无限接近于0

E全为1

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

当我们学完这一些就该回来看一下我们之前的题目了

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; 
}

当n=9时,二进制码为,0000000000000000000000000001001

而当用浮点数的方法提取出来时 .S = 0 ,M = 00000000000000000001001, E=-126(因为E存储的空间为全零,所以是1-127=-126)

-1^0 * 1.00000000000000000001001 * 2 ^-126 此值无限接近于零,而且打印时,只显示前六位小数,所以结果为 0.000000.

当我们使用 *pFloat = 9.0 时,它的二进制码就改变了, 9.0的二进制数位1001.0 转化为科学计数法为,1.001*2^3

此时S=0;

M=00100000000000000000000

E=3+127 = 130

完整的二进制码为:0 10000010 00100000000000000000000

如果我们使用%f打印,或是浮点型的输出占位符,输出结果就是9.000000

但如果使用整型的方式输出,它就会按整型的方式提取结尾也就成为了1091567616

0.000000`.

当我们使用 *pFloat = 9.0 时,它的二进制码就改变了, 9.0的二进制数位1001.0 转化为科学计数法为,1.001*2^3

此时S=0;

M=00100000000000000000000

E=3+127 = 130

完整的二进制码为:0 10000010 00100000000000000000000

如果我们使用%f打印,或是浮点型的输出占位符,输出结果就是9.000000

但如果使用整型的方式输出,它就会按整型的方式提取结尾也就成为了1091567616

本次分享到此结束,感谢大家的观看,若有错误,欢迎大家指正.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值