计算机如何来保存负数呢?其实只要达到这样的目的:正数负数都有一个唯一标识即可,但是,正如人类用+1和-1来表示可以提高效率一样,也得有一个比较适当的适合我们的计算机识别的一个方式。下面来详细讲解。

问题的由来

下面为了表示方便,先假设存储一个整型数字用4个bit。

举例来说,+2在计算机中表示为二进制的0010,那么-2怎么表示呢?

很容易想到,可以将一个二进制位(bit)专门规定为符号位,它等于0时就表示正数,等于1时就表示负数。比如,在8位机中,规定每个字节的最高位为符号位。那么,+2就是0010,而-8则是1010

更多的例子如下:

image

这就是直接用原码的方式来存储,虽然说这种方式理论上是可行的,毕竟每个数我都唯一标识了。

但是这种方式存在问题,我们希望+1和-1相加为0,但是通过这个方式算出来的是:0001+1001=1010 (-2)。也就是说,按照正常一步头的方式得不到我们想要的结果。

为了解决了“正负相加等于0”的问题,人们发明了反码。

反码

“反码”表示方式是用来处理负数的,符号位置不变,其余位置相反。

image

此时,我们再来算一下+1和-1相加,变成了0001+1110=1111,刚好反码表示方式中,1111象征-0。

此时,好像是解决了这个问题,但是我们发现,0这个时候有了两种表达:0000和1111。

即在用反码表示的情况下,0竟然可以用两个值来表示,这显然不好吧。毕竟+0和-0就是同一个玩意啊。

这个时候补码闪亮登场。

补码

很简单,在刚才反码的基础上加1。

image

此时,我们这里假定整形只有4位。那么-0表示为1111+1=10000,显然溢出了,就需要丢弃最高位,变成0000.

此时,神奇地发现,达到了统一,+0和-0都是用0000来表示了。

此时,也满足正负数相加为0的条件。比如+2为0010,-2为1110.此时两者相加为:0010+1110=(0)0000,丢掉最高位就是0000

那么对于普通情况,比如7+(-4)呢?即0111+1100=0011,就是3。OK,大功告成。

补码怎么求

上面已经说的很详细啦,比如-4,就是在4(0100)的基础上取反(1011)再加一(1100).

上面也解释了为什么要用补码。即保证了对称的正负数相加为0并且0只有一种表示方式。

还有一个重要的点就是,我们注意到,7-4其实我们都是转换成7+(-4),也就是说,在计算机中,减法都是用加法的逻辑实现的。

即:一套加法的电路实现加减法。此外,乘法和除法其实都是加法这套电路实现的。

补码的本质

这里假设存储一个整型用8个bit。

要将正数转成对应的负数,其实只要用0减去这个数就可以了。比如,-8其实就是0-8。

则8的二进制是00001000,-8就可以用下面的式子求出:

1
2
3
 00000000
-00001000
---------

因为00000000(被减数)小于0000100(减数),所以不够减。请回忆一下小学算术,如果被减数的某一位小于减数,我们怎么办?很简单,问上一位借1就可以了。

所以,0000000也问上一位借了1,也就是说,被减数其实是100000000,算式也就改写成:

1
2
3
4
100000000
-00001000
---------
 11111000

进一步观察,可以发现100000000 = 11111111 + 1,所以上面的式子可以拆成两个:

1
2
3
4
5
6
7
 11111111
-00001000
---------
 11110111
+00000001
---------
 11111000

通过这一步,我们就从数学上知道了为什么补码是取反加一了。

你看,求任何一个负数,都是0-正数,那么就用借位的思想来,则变成100000000。

100000000则可以分解为11111111+00000001。

此时求负数的过程就就变成11111111-X+1

  • 而先用11111111来减这个正数,这个结果就是对正数取反。
  • 此时再加上另外一个1.

这与我们求补码的过程是一样的,这也解释了为什么要这样求补码。

证明(可不看)

将上面的特例抽象一下,用统一表达式来证明一下。

我们要证明的是,X-Y或X+(-Y)可以用X加上Y的补码完成。

Y的补码等于(11111111-Y)+1。所以,X加上Y补码,就等于:

1
X + (11111111-Y) + 1

我们假定这个算式的结果等于Z,即

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

接下来,分成两种情况讨论。

  • 第一种情况,如果X小于Y,那么Z是一个负数。

由Y的补码等于(11111111-Y)+1,标记为F=(11111111-Y)+1,那么如何根据F逆向求Y呢?

1
Y=1111111-(F-1)

OK,因为此时Z是一个负数,那么Z进行补码的逆运算就可以求出它的绝对值,即正数。再加一个符号,两者相等。

1
Z = -[11111111-(Z-1)] = -[11111111-(X + (11111111-Y) + 1-1)] = X - Y
  • 第二种情况,如果X大于Y

这意味着Z肯定大于11111111,但是我们规定了这是8位机,最高的第9位是溢出位,必须被舍去,这相当于减去100000000。所以,

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

这就证明了,在正常的加法规则下,可以利用2的补码得到正数与负数相加的正确结果。换言之,计算机只要部署加法电路和补码电路,就可以完成所有整数的加法。

本文整理自: