一个小问题的解答

void main(){
    unsigned int a=3,b=4;
    printf("%d",(a-b)/2);
}

一个BC上面的小程序,有同学问我,为什么等于32767。考虑到大家后天就要考试了,在此我尽可能向大家把这个问题说清楚。

1 原码,反码和补码

大家使用的BC,里面的int在机器里面是用16位表示的,在下面的例子中,我都是以16位的数据存储格式来向大家说明。

原码

如果机器字长为n,那么一个数的原码就是用一个n位的二进制数,其中最高位为符号位:正数为0,负数为1。剩下的n-1位表示概数的绝对值。

计算机中,正数存原码,负数存补码!!

数的原码、反码和补码都是原码,或者说,正数只有原码!!当题目要你求一个正数的原码、补码或者反码时,只要算原码就可以了!!!

例1. 5[原] = 0000,0000,0000,0101;一共16位,其中第一位是符号位“+”,实际存储中,左边第一位使用0来代表“+”。

例2. -5[原] = 1000,0000,0000,0101;一共也是16位,其中第一位为符号位“-”,实际存储中,左边第一位使用1来代表“-”。

反码

反码就是在原码的基础上,符号位不变其他位按位取反(就是0变1,1变0)就可以了。只对负数有用!

例3 5[反]= 0000,0000,0000,0101;前面说了,正数的反码就是原码!

例4 -5[原] =1000,0000,0000,0101;因此,符号位的1不变,其他全部取反,得到了 -5[反]=1111,1111,1111,1010;

补码

补码也非常的简单就是在反码的基础上按照正常的加法运算加1。只对负数有用!

例5 5[补]=0000,0000,0000,0101;前面说了,正数的补码就是原码!

例6 -5[反]=1111,1111,1111,1010;因此,补码是在反码的基础上加1,得到了 -5[补]=1111,1111,1111,1011;

2 有符号数和无符号数

刚才介绍的原码、反码和补码。正数原码,负数补码就有符号数的计算机存储格式。而对于无符号数,无符号数都是正数,因此,无符号数在计算机中存储时,是没有符号位的!有符号数存储时,是1位符号位,15位数据位。无符号数直接就是16位数据位,直接转换成2进制即可!也因为这样,无符号数的可储存最大值是有符号数的2倍+1。

不管是有符号数还是无符号数,在计算机中,都存成了16位的结构。因此,同样的一串二进制,翻译成有符号数和翻译成无符号数可能就代表不同的数字!下面我将举例说明:

例7  0000,0000,0000,0001在有符号和无符号中各代表什么数?

解:无符号中,直接把这16位看成2进制数,转换成10进制后,就是1。在有符号中,第一位是0,剩下的15位转化为2进制,仍然是1。

总结:当第一位是0时(也就是有符号数为正数时),16位比特位翻译成有符号数和无符号数,代表的是同一个数。

例8 1000,0000,0000,0001在有符号和无符号中各代表什么数?

解:对于无符号数,注意,第一位不是符号位,而是代表2^15,因此,翻译成无符号数的值是2^15+2^0=32769。而对于有符号数,第一位是符号位,代表负“-”。因此,这是一个负数存成了补码的形式。如何将这个补码翻译成这个负数的值。我们需要反向操作。负数存成补码的过程是负数-原码-反码-补码,这里,反向操作,将这串二进制减1、取反则变成了原码。负数的原码就是1111,1111,1111,1111;注意,取反的时候符号位也是不要动的!原码的第一位是符号位“-”,实际值就是2^14+2^13····+2^1 = 32767

3 问题解答

有了上面的知识点,我再来将这道题的道理,相信大家就应该比较容易明白了。
对于 printf("%d",(a-b)/2);
1、先是计算a-b。
这里a是3,b是4,a-b是=-1;-1在计算机中怎么表示?使用补码,也就是对源码1000,0000,0000,0001 进行取反:1111,1111,1111,1110;再加1:1111,1111,1111,1111。因此,这一步的结果在计算机里的表示为 1111,1111,1111,1111。
2、计算“/2”
计算机运算“/2”操作的根本原理是数据右移1位!也就是将1111,1111,1111,1111整体右移一位,由于是无符号数,没有符号位,最左边空出来的那一位填0,最右边多出来的“1”舍去。得到了最终结果0111,1111,1111,1111。翻译成无符号数,就是2^14+2^13····+2^1 = 32767。
与答案相符合。

4 增补拾遗

这道题讲道理还是有点难的=。=我觉得大家搞不定也不要紧,也就两分,大部分的题目没这么难,这其实已经算是计算机组成原理的课程了,有点超纲。下面简单介绍一点额外的知识。

补全

补全分为有符号补全和无符号补全,对于无符号数的左移和右移,空出来的皆补0.而对于有符号数,右移之后,左边空出来的补与符号相同;左移时符号位不变,右边补0。(不用记了  用不到的···)

整数除法

两个int相除,也就是取模(与余数不同,是向0靠拢,具体百度),1/2=0,2/2=1,3/2=1;-1/2=0;C语言中,刚才我"/2"用的是右移操作,直接将1步的 1111,1111,1111,1111 翻译成无符号数65535,然后取模计算“/2”得到32767是一样的操作。

结束~各个满分不谢=。=

猜你喜欢

转载自blog.csdn.net/u010771890/article/details/73743465