浮点数在内存中的存储

目录

一.回顾:整数在内存中的存储

二.大小端字节序和字节序的判断

什么是大小端 ?

 为什么需要有大小端之分呢?

 判断当前机器的字节序

三.练习 

四.浮点数在内存中的存储 

 数字M

数字E

浮点数取的过程

E不全为0或者E不全为1

 E全为0

E全为1


一.回顾:整数在内存中的存储

整数的二进制表示方式有三种:即原码,反码,补码。

三种表示方式均有符号位和数值位两个部分 ,符号位用0表示正,用1表示负,二进制的最高位被当作是符号位,剩余的就是数值位。

正整数的原,反,补码都相同。

负整数的三种方式不同。

  1. 原码:直接将数字按照正负数的形式翻译成二进制得到的就是原码。的
  2. 反码:符号位不变,其他位依次按位取反就可以得到反码
  3. 补码:反码加1得到的就是补码。s

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

(详细的讲解可以看我的另一个文章:http://t.csdnimg.cn/LIINU)

二.大小端字节序和字节序的判断

当我们了解了整数在内存中的存储后,我们调试看一个细节:

#include<stdio.h>
int main()
{
	int n = 0x11223344;

	return 0;
}

 运行这个代码,然后我们再进调试。0x+数字,表示这个数字是以十六进制写的。

int类型是4个字节,32个比特位,我们知道一个十六进制的数字可以换算成4个二进制的数字。这里0x11223344,刚好就八位十六进制,也就是32位二进制数据,完全占据这个int。

并且每两个数字结合在一起占据一个字节。也就是11占一个字节,22占一个字节......

在调试时,我们通过内存和监视观察这个int。

 我们会发现在n这个整形中,数据的存储也是44 33 22 11这样的数字。但是我们发现

数据是这样进行存储的,倒着进行存储的,int的四个字节中的第一个字节存储的不是11而是44. 

比如这样: 

什么是大小端 ?

当数据在内存中的大小超过一个字节的时候,这时候就有了存储顺序的问题,根据不同的存储顺序,分为了大端字节序存储和小端字节序存储。

  1. 大端字节序存储:一个数据的低位字节的内容保存在高地址处,而高位字节的内容保存在低地址处,我们就将其称为大端字节序存储
  2. 小端字节序存储:一个数据的低位字节的内容保存在低地址处,而高位字节的内容保存在高地址处,我们就将其称为小端字节序存储

 为什么需要有大小端之分呢?


这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着⼀个字节,⼀个字节为8bit 位,但是在C语⾔中除了8 bit 的 char 之外,还有16 bit 的 short 型,32 bit 的 long 型(要看
具体的编译器),另外,对于位数⼤于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度⼤于⼀个字节,那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了⼤端存储模式和⼩端存储模式。 

 判断当前机器的字节序

#include<stdio.h>
int check_sys()
{
	int n = 1;
	return *((char*)&n);
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

 1的十六进制是0x00000001,如果机器是小端存储,那么int的第一个字节存储的就是01,不然就是00;根据这个我们就可以判断这个机器是什么字节序存储。

三.练习 

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

 %d是以十进制打印的。

	char a = -1;
	//10000000 00000000 00000000 00000001  -1原码
	//11111111 11111111 11111111 11111110  -1反码
	//11111111 11111111 11111111 11111111  -1补码
	//char只有八个bit位
	//11111111  补
	//10000000  反
	//10000001  原
	//还是-1
	signed char b = -1;
	//char就是signed char所以更上面一样。
	unsigned char c = -1;
	//11111111
	//因为是无符号的,所以原反补相同。
	//二进制的1111 1111是十进制的255

	//还有一种理解就是unsigned char的取值范围是0-255.
	//-1就是0-1,所以就是255
	//如果是-2,就是254
	//同理如果是256就是0
#include <stdio.h>
int main()
{
 char a = -128;
 printf("%u\n",a);
 return 0;
}
	char a = -128;
	//10000000 00000000 00000000 10000000
	//11111111 11111111 11111111 01111111
	//11111111 11111111 11111111 10000000
	//只留8个bit位
	//1000 0000 补码 --- a
	//char类型的数据要发生整形提升,有符号位补符号位
	//11111111 11111111 11111111 10000000
	//%u的形式打印,是认为a中存放的是无符号数

 

打印结果也就是4294967168. 

#include <stdio.h>
int main()
{
	char a = 128;
	//0000 0000 0000 0000 0000 0000 1000 0000 
	//只有8个
	//1000 0000 --- a
	//1111 1111 1111 1111 1111 1111 1000 0000 整形提升
	//%u以无符号整形十进制打印
	printf("%u\n", a);
	return 0;
}

结果和上面那个一样的。

四.浮点数在内存中的存储 

浮点数家族包含:float,double,long double类型。

浮点数表示范围:float.h中定义

首先我们先来观察一段代码:

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

 

n的值是9,所以第一行打印了9.通过*我们将float所指向的空间的值改为了9.0,所以*float是9.0.

而其他两个为啥是这样的情况呢?

很明显float类型数据在内存中的存储是与整数不同的。

根据国际标准IEEE(电⽓和电⼦⼯程协会) 754,任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式: 

十进制的5.0,写成二进制是101.0,相当于1.01 * 2 ^ 2
那么按照上面的规则S = 0 ,E = 2,M = 1.01;
十进制的-5.0,写成二进制是-101.0。相当于-1.01 * 2 ^ 2
那么按照上面的规则S = 1 ,E = 2, M = 1.01 

对于浮点数来说,他们在内存中的存储并不是纯的二进制,而是S E M.

对于32位的浮点数来说,最高的一位存储符号位S,接着的8位存储E,最后的23 位存储M

对于64位的浮点数来说,最高的一位存储符号位S,接着的11位存储E,最后的52位存储M.

 

 数字M

前面说过M的取值范围是[1,2)。也就是所M可以写成1.xxxxxxxx的形式,其中xxxxxxxx表示小数部分。在计算机保存M的时候,默认第一个数总是1,因此我们可以舍去,只保留后面的小数部分。在读取的时候,我们再加上1,这样的目的是为了节省一位,这样就可以多保留一个数字了。

数字E

首先这个规定,E是一个无符号整形的数字。这意味着E如果是8位,取值范围就是0-255,E如果是11位取值范围就是2047,但是我们只科学计数法中E是可以出现负数的。所以规定再存入E的值的时候必须加上一个中间数,对于8位的E来说这个中间数是127,对于11位的E来说这个数是1023

浮点数取的过程

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

E不全为0或者E不全为1

这时候就采取 指数E的值减去127(或者1023)得到真实值,再将有效数字M前加上一位1

0.5的存储 

0.5   十进制
0.1   二进制
1.0 * 2 ^-1
S = 0
E = -1
M = 1.0
存入E的时候就是(-1 + 127) = 126
表示为0111 1110
所以0.5的二进制表达方式为0 01111110 00000000000000000000000

 E全为0

 这时,浮点数的指数E等于1-127(1-1023)即为真实值,有效数字M不在加上第一位的1,而是还原为0.xxxxx这样的小数,这样的数字写出来是为了表示+-1(取决于符号位),这样的数字无限接近于0.

E全为1

这时,如果有效数字M不全为0,表示+-无穷大(取决于符号位)。

5.5在内存中的存储

int main()
{
	//5.5
	float f = 5.5f;
	//5.5
	//101.1
	//1.011*2^2
	//(-1)^0 * 1.011 * 2^2
	//S=0
	//M=1.011
	//E=2
	//2+127=129
	//0 10000001 01100000000000000000000
	//01000000 10110000 00000000 00000000
	//40         B0       00       00
	//
	return 0;
}

回到我们一开始看到题目。

#include <stdio.h>
int main()
{
	int n = 9;
	// 00000000 00000000 00000000 00001001
	
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	//以浮点数的视角读取的话
	//0 00000000 00000000000000000001001
	//E为全0,所以这是一个无限接近0的数字。
	printf("*pFloat的值为:%f\n", *pFloat);
	*pFloat = 9.0;
	//1001.0
	//1.001 * 2 ^ 3
	//S = 0
	//E = 3
	//M = 1.001
	//0 1000 0010 00100000000000000000000
	printf("num的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值