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

整数在内存中的存储

 首先我们要知道 整数的2进制表⽰⽅法有三种,即原码、反码和补码

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

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

负整数的三种表⽰⽅法各不相同。

原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。

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

补码:反码+1就得到补码。对于整形来说:数据存放内存中其实存放的是补码。

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

我认为通过几段代码分析可能会更加好理解整数是如何在内存中存储的

看上面那段代码

1.首先我们要知道char 是占一个字节,八个bit位,而signed是修饰的有符号的,unsigned是修饰的无符号的,至于char是有符号还是无符号是取决于编译器的,而vs中是修饰的有符号的

2.所以char a类型可能存放的所有二进制序列有00000000 00000001 00000010.......10000000 10000001 10000010....11111110 11111111,这里所有的可能性有2的8次方种可能性即256种可能性

3.signer char就可以根据所学知识来分析了 最高位为符号位 0代表正数 1代表负数 剩下的七位为数值位,而内存中存的数据是补码,0开头的读取的是正数,正数的原码、反码、补码都相同,所以0开头一直到1开头为止他们的原码为0 1 2 3 4 5......126 127;剩下的1开头的如何分析呢,首先符号位为1代表负数,而我们假设已知补码是11111111,由原码等于反码+1可知,取反(符号位不变)10000000 +1后为10000001(得到原码)为-1,所以以此类推可得-1 -2 -3.......-127 -128。所以有符号类型的取值范围为-128~127

4.unsigned char 无符号位就显而易见了,00000000没有符号位,8个都是数值位,所以依次是0 1 2 3 4......254 255 ,所以unsigned char的取值范围为0~255

分析了这些基础知识后,我们来对这段代码进行解读

char a=-1

-1是个整数,放在变量a中,里面的二进制序位为32个bit位10000000000000000000000000000001 10000000000000000000000000000001这个是-1的原码,而在内存中的是补码为11111111111111111111111111111111,但是char是字节类型只能存放8个bit位即11111111

signed char b=-1与上同理,b存入的是11111111

unsigned char c=-1可能会有疑问担心不是说是unsigned的范围是0~255吗,但这里我们不用考虑,先给c存入-1去计算,c存入的也是11111111

但是存入后通过a,b,c前面所修饰的类型导致他们所对应的就会有不同

首先a=%d打印,是打印的一个有符号的整数,但是a是一个字符,不相符合,所以要提升后再打印,整形提升要补符号位,所以提升后a为11111111111111111111111111111111,这是补码,而%d是打印的原码,所以它的原码又变成了10000000000000000000000000000001  即   -1

b=%d与上文a的打印方式同理

c=%d这个就有区别了,11111111是无符号char提升的话要补0,则为00000000000000000000000011111111,%d打印有符号的整数,此时0代表正数,正数的原码、补码 、反码都相同,所以打印出来为255

此代码打印出来的结果就是-1 -1 255。通过这个题就可以然我们对整数在内存中如何存储有个很好的了解

#include <stdio.h>
int main()
{
 char a = -128;
 printf("%u\n",a);
 return 0;
}

接着再来分析一道

首先需要记住的是%u不同于%d,%u打印的是个无符号的整数

简要分析一下,char a中存放的是10000000(计算后),然后进行整形提升,char有符号补1,即11111111111111111111111110000000,而%u打印的是无符号整数,所以开头的1视为数值位,不存在原码、反码、补码的问题,即原码补码反码相同所以直接打印这个数(一看就非常大了),结果也是个很大的数。

接下来我们看浮点数在内存中的存储

V   =  (−1) ^S *M*2 ^E

(−1) ^S表⽰符号位,当S=0,V为正数;当S=1,V为负数

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

 2^E 表⽰指数位

举个例子十进制的5.5

5.5将其转换为二进制为101.1,用科学计数法表示为1.011,1.011*2^2

V=(-1)^0*1.011*2^2,S=0,M=1.011

对于32位的浮点数,最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M,浮点数的存储存储的就是S,M,E相关的值

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

IEEE754对有效数字M和指数E,还有⼀些特别规定。

前⾯说过, 1≤M,也就是说,M可以写成 1.xxxxxx 的形式,其中 xxxxxx 表⽰⼩数部分。 IEEE754规定,在计算机内部保存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是可以出现负数的,所以IEEE754规定,存⼊内存时E的真实值必须再加上 ⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。⽐如,2^10的E是 10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

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

 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,则其⼆进制表⽰形式为:

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

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

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

通过这段代码来深入了解浮点数如何在内存中存储

1.int n=9;9的二进制序列为00000000 00000000 00000000 00001001(此时n的空间所存储),以整数的形式存,又以整数的形式取出,第一个代码打印为9

2,取n的地址存入pFloat里面,在pFloat的角度看待其是浮点数的存储,其会这样解读这32个bit位,0 00000000 00000000000000000001001,三段分别为S M E,当内存中E为全零时,真实的E为1-127=-126,有效数字M不会再加上1,取出后0.0000000000000000001001  所以存储的值为(-1)^0*0.0000000000000000001001 *2^-126;%f默认打印小数点后六位,这个值非常小所以只能打印出0.000000

3.*pFloat,将pFloat解引用得到这块整形空间存储的00000000 00000000 00000000 00001001,以浮点数的视角存储9.0(10)二进制1001.0     1.001*2^3 (-1)^0*1.001*2^3 S=0,M=1.001,E=3存入E3+127=130   0 10000010 00100000000000000000000(此时n的空间所存储),接下来以整数的形式去取出来,首先存的是补码,其次%d是存储的有符号,0为正数,原码、反码、补码都相同,直接打印出值1091567616

4.浮点数存储,浮点数取出,打印出来为9.0

上述就是有关整数和浮点数在内存中的存储

  • 10
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【问题描述】 封装一个模板数组类Array,支持以下操作: 1. 构造函数Array(int n),将数组初始化为n个存储空间,建议使用vector; 2. 函数input(int n),使用插入运算符<<读取数据,最多读取n个元素,但不能超过数组存储空间的上限; 3. 重载下标运算符,返回数组的元素。 封装一个分数类Fract,用来处理分数功能和运算,能支持你的Array类使用。 1. 构造:传入两个参数n和m,表示n/m;分数在构造时立即转化成最简分数。 提示:分数化简有专门的算法,可自行调研 2. show()函数:分数输出为“a/b”或“-a/b”的形式,a、b都是无符号整数。若a为0或b为1,只输出符号和分子,不输出“/”和分母。 3. 在分数类上重载+=运算符,进行分数的加法运算。 【输入形式】 输入为两部分,分别是一组实数测试样例和一组分数测试样例。 这两组测试样例都以正整数n,且n小于1000,n表示需要输入n个实数(或分数)。 测试样例的第二行开始为n个实数(或分数)。其每个分数输入为两个整数n、m,表示分数n/m。 【输出形式】 第一部分输出一个实数,是第一组测试样例之和;第二部分输出一个分数,是第二组测试样例之和。 分数输出时为最简形式,负号只会出现在最前面,若分母为1或分子为0,则只输出一个整数,即分子部分,而没有“/”和分母部分。 【样例输入】 4 6 8 7 5 9 1 3 20 -15 80 150 -9 1 6 6 12 16 -33 -48 6 11 0 -10 【样例输出】 26 -17117/2640 25.00 下载源文件 得分25.00 最后一次提交时间:2021-06-08 21:49:12 共有测试数据:5 平均占用内存:1.415K 平均运行时间:0.00648S 测试数据 评判结果 测试数据1 完全正确 测试数据2 完全正确 测试数据3 完全正确 测试数据4 完全正确 测试数据5 完全正确 详细 
11076 点数的分数表达 时间限制:1000MS 内存限制:65535K 提交次数:0 通过次数:0 题型: 编程题 语言: 无限制 Description 在计算机,用float或double来存储小数有时不能得到精确值,若要精确表达一个点数的计算结果, 最好用分数来表示小数,有限小数或无限循环小数都可以转化为分数,无限循环小数的循环节用括号标记出来。如: 0.9 = 9/10 0.(3) = 0.3(3) = 0.3(33) = 1/3 当然一个小数可以用好几种分数形式来表示,我们只感兴趣最简的分数形式(即分母最小),如: 0.3(33) = 1/3 = 3/9 因为任何一个数都可以转化为一个整数和一个纯小数之和,整数部分较为简单无需做额外处理,只要将纯小数部分转化为分数形式,整数部分的分数部分就很简单了。 现在给定一个正的纯小数(这个纯小数为有限小数或无限循环小数),请你以最简分数形式来返回这个纯小数。 Input 给定一个纯小数,若是无限循环小数,用括号标记循环节,输入小数表达不超过100个字符。Output 输出:化为最简分数形式,分子在前,分母在后,间空格连接。 Sample Input 0.3(33) Sample Output 1 3 Hint 此题题目规定:输入小数表达不超过100个字符。 如此长的数,本意要大家用高精度数的运算来求解. 但后台测试数据没有做如此之长,放松一些吧,用64位整数也是允许通过的! 此题采用字符串接收输入,大家在接受数据的时候,不要用(c=getchar())!='\n'诸如此类一个字符一个字符接受, 然后判断是否是回车符号来接受输入,这样的方式在你本机运行不会有问题,但OJ系统会有错误,无法输出结果, 因为测试平台行末并非'\n'字符。这里接受数据用scanf的%s,或cin等,会自动判别回车字符的,不要在你程序里去专门判别或吸收回车字符。 char a[105]; scanf("%s",a); 或cin >> a; 解题思路: 考虑输入的是纯小数,先暂时不考虑分子和分母有公因子的情况。 (1) 假设有限小数:X=0.a1a2…an,式的a1,a2,…,an都是0~9的数字。 X=0.a1a2…an=a1a2…an/10^n (2) 假设无限循环小数:X=0.a1a2…an(b1b2…bm),式的a1,a2,…,an, b1,b2,…,bm都是0~9的数字,括号为循环节。 第一步,先将X化为只有循环部分的纯小数。 X=0.a1a2…an(b1b2…bm) (10^n)*X=a1a2…an+0.(b1b2…bm) X=(a1a2…an+0.(b1b2…bm))/(10^n) 上式,a1a2…an是整数部分,容易解决。重点考虑小数部分0.(b1b2…bm)如何化为分数形式,再加上整数部分即可。 第二步,考虑Y=0.(b1b2…bm),将Y化为分数, (10^m)*Y=b1b2…bm+0.(b1b2…bm) ((10^m)-1)*Y=b1b2…bm Y=b1b2…bm/((10^m)-1) 将第二步的Y带入第一步的X,可得: X=(a1a2…an+Y)/(10^n)=((a1a2…an)*((10^m)-1)+(b1b2…bm))/((10^m)-1)*(10^n) 此时,可以将任何一个有限小数或无限循环小数,化为分数表示。 但此时的分子分母未必是最简化的,对分子分母再进行约分, 删去公共的因子,A/B=(A/GCD(A,B))/(B/GCD(A,B)),化为简单形式。 思路如上,但实现上,所有分子分母的变量,以及求最大公约数,都须用64位整数。 编译环境不同,对64位整数的定义和输入输出略有不同: 1)gnu gcc/g++ long long类型,或unsigned long long, 输入输出用cin和cout直接输出,用scanf和printf也可以的。 long long a; cin >> a; cout << a; 也可以使用 scanf("%lld",&a); 或scanf("%I64d",&a); printf("%lld",a); 或printf("%I64d",a); 2)vc用__int64类型,或unsigned __int64 scanf("%I64d",&a); printf("%I64d",a); vc下,64整数不要用cin和cout来输入输出,据说vc下64位整数兼容不好,会出错,我未测试过大家可测试一下如下程序会出错否。 __int64 a; cin >> a; cout << a;

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值