代码改变世界

二进制的补码

2016-11-17 18:03  ZengGW  阅读(31670)  评论(0编辑  收藏  举报

今天在学习C Primer Plus(第五版)中文版.pdf的时候遇到这么个问题,先上代码:

#include <stdio.h>
#define PAGES 336
#define WORDS 65618
int main(void)
{
        short num = PAGES;
        short mnum = -PAGES;

        printf("num as short and unsigned short:%hd %hu\n",num,num);
        printf("-num as short and unsigned short:%hd %hu\n",mnum,mnum);
        printf("num as int and char:%d %c\n",num,num);
        printf("WORDS as int,short,and char:%d %hd %c\n",WORDS,WORDS,WORDS);return 0;
}

结果:

num as short and unsigned short:336 336
-num as short and unsigned short:-336 65200
num as int and char:336 P
WORDS as int,short,and char:65618 82 R

请看我标红的部分,其实我蛮想不明白的,为什么-336的无符号整数是65200呢?书上说是2的补码(书中描述):数字0到32767代表它们本身,而数字32768到65535则代表负数,65535代表-1,65534代表-2,依次类推,因此-336由65536-336,也即65200来表示;本宝宝表示真心没看懂啥意思,然后就在网上各种找二进制的补码是啥意思,下面咱们就来看看什么是二进制的补码

我试验的计算及环境假设是8位的,那接下来我就拿计算机是8位作基础来讲解:

一、负数在计算机中如何表示

  之前我写了一篇博客,里面是计算c语言整数类型的取值范围的额,二进制中区分正负数的方法是看二进制的最高位是0还是1,1为负数,0为正数

  比如:127的二进制是01111111,而-127的二进制是 10000001(011111111 - 先取反,再加1 ->10000000+1 = 10000001),从这里可以看出127的最高位是0,而-127的最高位是1

二、什么是二进制补码

  取一个数的二进制补码需要两步:

  1>.每个二进制位都取相反的值,也就是二进制位是1的,补码就是0,二进制位是0的,补码就是1

  2>.再把取反的二进制数转换成十进制,加上1,最后的结果就是这个数的补码的十进制数

  举例:取-127的二进制补码(8位机)

    二进制数:01111111

    补码:10000000

    结果:补码 10000000 + 1 = 10000001(129)

    也就是说-127在计算机(8位机)中10000001(写到这里,我突然有点明白C Primer Plus书中描述的啥意思:0-127代表它本身,128-255代表负数,那-127就等于256-127=129

  那可能很多人会说:为什么是这样啊?虽然知道怎么计算了,但是不知道为什么是这么计算的,那么接下来就来说说二进制补码的原理

三、二进制补码的原理

  那大家都知道负数怎么来的,比如:A-B,那A比B小,结果就会是负数,这就有很多情况了,咱不讨论,就比如给你一个负数,那么最直接的你肯定会想到一个表达式了,比如给你个-127,那表达式就是0-127的来的,那我们把他转换成二进制来运算一下:

  预想结果:-127

  十进制表达式:0-127

  二进制表达式:00000000(0) - 01111111(127)

  以前小学数学当被减数大于减数的时候都要向上借一位来减的,那么接下来就是借位(

  重点:这里计算的时候二进制的位数是有规定的,比如-127,它是1111111 由7个1的二进制,那同学就用7个0的二进制去减,结果借一位变成了10000000(128)-1111111(127) = 1,然后在加1等于2,结果-127的补码就是00000010(2),这样可是错的,一定要遵守一个标准,那就是当前计算机是多少位的就借多少位加1位,那么比如-10是1010,就是00001010,借位就得在第9位上借,也就是100000000,结果就是246(11110110)

  借位进行运算:

    1>.100000000(256) - 01111111(127) = 10000001(129)

    2>.100000000(256) = 11111111(255) + 1;

      11111111(255) - 01111111(127) = 10000000(128)

      10000000(128) + 1 = 10000001(129)

  上面就是二进制补码的简单计算过程

四、二进制补码有哪些好处呢?

  感觉不就是一个负数而已,非要搞的这么弯弯绕,我反正是晕了,那接下来就看下二进制补码的好处

  那我们之前说的判断最高位是1还是0来区分正负,那接下来我们就用两种表示法来计算作比较:

  举例:-10

  表达式:20 + (-10)

  1>.最高位区分正负:-10的二进制数为10001010

    二进制计算:00010100 + 10001010 = 10011110

    10011110转换成十进制是30,根据最高位区分正负,结果就是-30

  2>.再来看看二进制补码的方式进行计算:-10的二进制补码(11110101 + 1 = 11110110(246))

    二进制计算:00010100 + 11110110 = 100001010 

    而咱们刚才已经说了,假设计算机是8位的,那么这个结果超过8位,第九位会被舍弃,也就是00001010,结果就是10

五、为什么正数加法适用于二进制补码呢?

  接下来我们求证一下X-Y(x + (-Y))这个表达式,相信大家就明白了(8位机)

  Y的二进制补码由上面的讲解大家都知道是:(11111111 - Y) + 1 ,所以也就是X加上Y的二进制补码,表达式可以写成如下格式:X + (11111111 - Y) + 1

  得到这个表达式就好办了,接下来我们分成两种情况来解释:

  1>.那就是X小于Y,那么结果肯定是个负数了,我们采用二进制的补码的逆运算,求出它对应的正绝对值,再在前面加一个负号就可以了

    第一步:计算Z、X、Y的二进制补码的表达式 Z = -((11111111 - Z) + 1);X = (11111111 - X) + 1;Y = (11111111 - Y) + 1;

    第二步:根据表达式X + (11111111 - Y) + 1来替换计算:-( ( ( 11111111 - X) + 1 ) - ( ( 11111111 - Y ) + 1 ) ) = -(11111111 - X + 1 - 11111111 + Y - 1) = -( -X + Y) = X - Y; 

  2>.X大于Y,那结果肯定是正数,那意味着Z肯定大于11111111,那根据8位机,第九位溢出了,就要舍去,表达式为(不太明白...):

    Z = Z - 100000000 = X + (11111111 - Y) + 1 - 100000000 = X - Y;

  文章出自:http://www.ruanyifeng.com/blog/2009/08/twos_complement.htm