



刚才没有详细讲解右移运算,是因为在右移运算中空出来的高位上填充的数字有 0 和 1 两种情况。要区分这两种情况,需要先了解一下二进制中如何表示负数。内容稍微有点长,我们先看表示负数的方法,然后学习右移运算的方法。
要在二进制中表示负数,一般的方法是将最高位用来表示符号,这时最高位被称为符号位。我们可以约定, 符号位 为 0 时表示正数,符号位为 1 时表示负数。按照这样的约定,- 1 用 8 位二进制数应该如何表示呢?可能有人会说:“1 用二进制表示是 00000001,那么 - 1 自然是 10000001 了。”很遗憾,这个答案是错误的。- 1 用 8 位二进制数表示应该是 11111111。
计算机在进行减法运算时,其内部实际上进行的是加法运算。也就是说,计算机是用加法运算来实现减法运算的。为了实现这一点,在表示负数的时候,我们需要使用“2 的补码”这一特殊的方法。 2 的补码 是在二进制中用正数来表示负数的一种神奇的方法。
    要得到 2 的补码,我们需要先将二进制数的各位数字反转
    
    ,然后再将结果加 1。例如,将 - 1 用 8 位二进制数表示,就相当于求 1(即 00000001)的 2 的补码。00000001 的 2 的补码,就是将其各位数字中的 0 变成 1,1 变成 0,然后将得到的结果加 1,也就是 11111111(
    
     图 2-5
    
    )。
   
    图 2-5 求 00000001 的 2 的补码的方法
大家可能很难直观地理解 2 的补码这种方法,但它实际用起来非常完美。假设我们要计算 1 - 1,也就是计算 1 + (- 1),答案显然是 0。首先我们将 - 1 表示成 10000001 来试试看(错误的方法)。“00000001 + 10000001”的结果是 10000010,显然不是 0( 图 2-6 ),因为所有位上的数字都是 0 才表示 0。
    图 2-6 用错误的方法表示负数的情况
接下来我们将 - 1 表示成 11111111 来试试看(正确的方法)。“00000001 + 11111111”的结果正好是 0(= 00000000)。在这个运算中,最高的第 9 位的数字溢出了,我们之前讲过,计算机会舍弃溢出的数字,因此在 8 位范围内计算的话,100000000 这个 9 位二进制数就会变成 00000000( 图 2-7 )。
    图 2-7 用正确的方法表示负数的情况
    对于求 2 的补码的方法,大家可以用“反转 + 1”的口诀来记。为什么使用 2 的补码就可以正确表示负数呢?通过图 2-7 应该可以看出,将一个二进制数反转后再加 1,然后和原数相加的结果一定为 0
    
    。请大家先尝试用 1 和 - 1 的二进制为例,讲一讲 2 的补码的原理。不仅是 1 + (- 1),对于 2 + (- 2)、39 + (- 39) 等其他的数,要想让结果为 0,都需要使用 2 的补码。
   
当然,结果不为 0 的运算,使用 2 的补码也可以得到正确的结果。但需要注意的是,如果运算结果是负数,那么这个负数也是以 2 的补码来表示的。我们以 3 - 5 这个运算为例。3 表示成 8 位二进制数是 00000011,5(= 00000101)用 2 的补码表示就是“反转 + 1”,即 11111011,因此 3 - 5 就相当于“00000011 + 11111011”。
“00000011 + 11111011”的运算结果是 11111110,其最高位为 1。由此可知,它表示的是一个负数。大家知道 11111110 代表负几吗?这里我们可以利用负负得正的性质。如果 11111110 是负 ××,那么 11111110 的 2 的补码就是正 ××,因此只要求 2 的补码的补码,就可以得到其绝对值。11111110 的 2 的补码,就是反转再加 1,得到 00000010,也就是十进制的 2。因此,11111110 就代表 - 2,与 3 - 5 的结果正好吻合( 图 2-8 )。
    图 2-8 3-5 的运算结果
    在编程语言提供的整数类型
    
    中,有些可以处理负数,有些不能处理负数。例如,C 语言的数据类型中,有不能处理负数的 unsigned short 型,也有能处理负数的 short 型。这两种类型的变量长度都是 2 字节(= 16 比特),都能表示 2 的 16 次幂 = 65536 种不同的值。但是,它们能表示的值的范围不同,short 型是 - 32768~32767,而 unsigned short 型是 0~65535。这是因为 short 型将最高位为 1 的值按照 2 的补码来处理,而 unsigned short 型则将其作为 32768 以上的正数来处理。
   
认真思考 2 的补码的原理,我们就可以理解为什么在 - 32768 ~ 32767 这个范围中,负数比正数要多一个。这是因为最高位为 0 的数有 0~32767,共 32768 个,其中已经包含了 0,而最高位为 1 的数都是负数,即 - 1~- 32768,也是 32768 个,其中不包含 0。也就是说,由于 0 被包含在正数的范围内,所以负数比正数要多一个。尽管 0 不是正数,但从符号位的角度来看,它和正数属于同一类。