整型与浮点型在内存的存储方式

前言

我们这次的文章的主要目的是让大家认识到整型与浮点型它们的不同之处,让大家在进行运算时

或者是类型转换时能够更为恰当,当然对于浮点数的储存,大家只需了解即可

1整型的存储方式

整型有 int    long long  short char 等类型

整型主要有两种两种不同的存储方式

一种是无符号型

另一种是有符号型

有符号的整数,三种表⽰⽅法均有符号位和数值位两部分,符号位都是⽤0表⽰“正”,⽤1表 ⽰“负”,最⾼位的⼀位是被当做符号位,剩余的都是数值位

举例 char  在不说明有unsigned的情况下

char是有符号的整型 它占有1个字节  8个比特位

最高位表示正数与负数 有效数字为就是7个比特位

那么它可表示的范围为-2的7次方~~~2的7次方-1

总共为256个数字

当然在这之前要告诉大家 原码 反码 补码

正数的原码 反码补码都相等

有正数就有负数

负数在内存中是以补码来存储的,原码———反码 除符号位其余取反

反码————补码加上一个1

我们可以通过举例来向大家说明情况

假如我们有一个数 为-10,

提醒一下 8位一个字节

那么它的原码为    

10000000 00000000 00000000 00001010

反码为除符号位不变其余取反

11111111 11111111 11111111  11110101

补码就为 反码加1

11111111 11111111 11111111  11110110

我们知道在内存之中是用16进制来几录数据的

1111 1111 11111 1111 1111 1111  1111 0110

那么正常使用16进制来表示

就是 ff ff  ff f6

而在整型中 内存记录的数据为补码

在计算机系统中,数值⼀律⽤补码来表⽰和存储。 原因在于,使⽤补码,可以将符号位和数值域统⼀处理; 同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是 相同的,不需要额外的硬件电路

其实整型的内存存储很简单

2.⼤⼩端字节序和字节序判断

我们来看刚才我们求得了-10的补码的16进制的表示

那么接下来,我们可以看到代码中-10的真实存储

看图

大家看前8位

 f6  ff  ff  ff

和我们刚才所算的 ff   ff  ff   f6

刚好是相反的这是为什么呢

原因在于存储方式的不同

2.1认识大小端字节序存储

其实超过⼀个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分 为⼤端字节序存储和⼩端字节序存储,

下⾯是具体的概念: ⼤端(存储)模式: 是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处

⼩端(存储)模式: 是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存在内存的⾼地址处

那大家想一想看我们刚才的存储是什么模式

真实值

ff   ff  ff   f6

存储值

 f6  ff  ff  ff

地址一定是从低地址到高地址(从左往右)

一看就知道了 低位的f6存储在了低地址处,那么就是小端存储

目前大部分机器都是小端存储

2.2字节序的判断

那么各位能否写一个最简单的代码判断它的字节序存储方式呢

看代码

int main()
{
	int a = 1;
	//正常的16进制表示
	//00 00 00 01
	//小端存储
	//01 00 00 00
	//大端存储
	//00 00 00 01
	if (*((char *)(&a))== 1)
		printf("小端\n");
	else
		printf("大端\n");
	return 0;
}

这就是一个判断的最简单的方式,接下来进入最关键的地方

3浮点数的存储

这个问题很是关键,对于数据的处理来讲很关键

我们先讲它是如何存储的

在来看案例

在这里大家是一定会有收获的

常⻅的浮点数:3.14159、1E10等,浮点数家族包括: float、double、long double 类型。

那么接下来看

3.1浮点数的存储

根据国际标准IEEE(电⽓和电⼦⼯程协会) 754,

任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式: V   =  (−1) 的s次方 *M ∗ 2的E次方

 • (−1)S的s次方表⽰符号位,当S=0,V为正数;当S=1,V为负数

• M 表⽰有效数字,M是⼤于等于1,⼩于2的

• 2 的E次方      E表⽰指数位

⼗进制的5.0,写成⼆进制是 101.0 ,相当于 1.01×2^2

那么,按照上⾯写的格式,可以得出S=0,M=1.01,E=2

⼗进制的-5.0,写成⼆进制是 -101.0 ,相当于 -1.01×2^2 。那么,S=1,M=1.01,E=2

IEEE 754规定: 对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M

对于64位的浮点数,最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M

总而言之就是 s e m

通过科学计数法来存储

float类型为  1 8 23

double为   1 11 51

3.2特殊规定

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是可以出现负数的,所以IEEE 754规定,存⼊内存时E的真实值必须再加上 ⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。⽐如,2^10的E是 10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001

3.3浮点数的读取

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

1   E不全为0或不全为1

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

⽐如:0.5 的⼆进制形式为0.1,

由于规定正数部分必须为1,即将⼩数点右移1位,则为1.0*2^(-1),其 阶码为-1+127(中间值)=126,表⽰为01111110,⽽尾数1.0去掉整数部分为0,补⻬0到23位 00000000000000000000000,

则其⼆进制表⽰形式为: 0 01111110 00000000000000000000000

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

则其⼆进制表⽰形式为:

00000000 00100000000000000000000

3  E全为1 这时,如果有效数字M全为0,表⽰±⽆穷⼤(正负取决于符号位s);

则其⼆进制表⽰形式为:

0 11111111 00010000000000000000000

3.4示例

代码1
#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;
}

看看结果

我们来解释

看第一个结果为什么 9 还原成浮点数,就成了 0.000000 ?

9以整型的形式存储在内存中,

得到如下⼆进制序列: 0000 0000 0000 0000 0000 0000 0000 1001

⾸先,将 9 的⼆进制序列按照浮点数的形式拆分,得到第⼀位符号位s=0,后⾯8位的指数 E=00000000 , 最后23位的有效数字M=000 0000 0000 0000 0000 1001

V=(-1)^0 × 0.00000000000000000001001×2^(-126)=1.001×2^(-146)

V是⼀个很⼩的接近于0的正数,所以⽤⼗进制⼩数表⽰就是0.000000

主要是E全部为0

看第二个结果

浮点数9.0,为什么整数打印是 1091567616

⾸先,浮点数9.0 等于⼆进制的1001.0,

即换算成科学计数法是:1.001×2^3

所以: 9.0  =  (−1)   ∗ 0  (1.001)  ∗  23 ,

那么,第⼀位的符号位S=0,有效数字M等于001后⾯再加20个0,凑满23位,指数E等于3+127=130, 即10000010 所以,写成⼆进制形式,应该是S+E+M

    0 10000010 001 0000 0000 0000 0000 0000

这个32位的⼆进制数,被当做整数来解析的时候,就是整数在内存中的补码,原码正是 1091567616

这样就完美解决了问题

问题解决

请设计一个程序使它能够保留任意浮点值的两位小数

要求要四舍五入

在讲解这个的开始我们要给大家一个提示

浮点数在内存中不是精确存储的

举例

大家看到没有这个浮点数在内存中的存储是不精确的

但是这里还有一点要提醒大家

大家看 结果不应该是 314吗

原因是浮点值的精确值为10e-6如果两个浮点值的差值下与这个值那么

这两个数就会被编译器认为相等

3.1499999 与3.15相差10e-7比10e-6小默认他们相等

那么大家如果真的要给一个浮点值保留;两位小数,最好是把它换为整型

这样才好操作,看代码

float fun(float n)
{
	long long x = n * 1000;
	int a = x % 10;
	if (a >= 5)
		x = x - a + 10;
	else
		x -= a;
	n = x/1000.0;
	return n;
}
int main()
{
	while (1)
	{
		float n;
		scanf("%f", &n);
		float ret = fun(n);
		printf("%.2f\n", ret);
	}
	return 0;
}

我们这样就可以解决问题了

总结

笔者个人认为浮点数的存储比较的复杂

当然真正在敲代码时,这个其实用的是不多的

大家只要了解认识到了浮点数

尤其是浮点指针,不能够访问整型,他们的存储方式不同

这个就差不多了

最最要注意的是浮点数在内存中是无法精确存储的

如果拿捏不准请你把浮点数的值变为确定的整型数来处理

ok本次博客的料还是挺多的,祝大家开心

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值