二补数

二补数（2's complement）是一种用二进制表示有符号数的方法，也是一种将数字的正负号变号的方式，常在电脑科学中使用。二补数以有符号位元的二进制数定义。

正数和0的二补数就是该数字本身再补上最高位元0。负数的二补数则是将其绝对值按位取反再加1。

二补数系统的最大优点是可以在加法或减法处理中，不需因为数字的正负而使用不同的计算方式。只要一种加法电路就可以处理各种有号数加法，而且减法可以用一个数加上另一个数的二补数来表示，因此只要有加法电路及二补数电路即可完成各种有号数加法及减法，在电路设计上相当方便。

另外，二补数系统的0就只有一个表示方式，这和一补数系统不同（在一补数系统中，0有二种表示方式），因此在判断数字是否为0时，只要比较一次即可。

右侧的表是一些8-bit二补数系统的整数。它的可表示的范围包括-128到127，总共256（=28）个整数。

## 数字表示方式

### 说明

二补数｜十进制

0111｜7

0110｜6

...｜...

0010｜2

0001｜1

0000｜0

1111｜−1

1110｜−2

...｜...

1001｜−7

1000｜−8

以下用4位元的二补数数字来说明二补数系统的数字表示方式。

- 在表示正数和零时，二补数数字和一般二进制一样，唯一的不同是在二补数系统中，正数的最高位元恒为0，因此4位元的二补数正数，最大数字为0111 (7)。
- 二补数数字的负数，最高位元恒为1，4位元二补数的数字中，最接近0的负数为1111 (-1)，以此类推，因此绝对值最大的负数是1000 (-8)。

以上的表示方式在电脑处理时格外方便，用以下的例子说明：

```
    0011 (3)
  + 1111 (-1)

   10010 (2)
```

结果10010似乎是错的，因为已经超过四个位元，不过若忽略掉（从右开始数）第5个位元，结果是0010 (2)，和我们计算的结果一样。而且若可以将二进制的1111 (-1)变号为0001 (1)，以上的式子也可以计算减法：3-1 = 2。

在n位元的二补数加减法中，忽略第n+1个位元的作法在各种有号数加法下都适用（不过在判断是否溢位（overflow）时，仍然会用到第n+1个位元）。因此在二补数的系统，加法电路就可以处理有负数的加法，不需另外处理减法的电路。而且，只要有电路负责数字的变号（例如将1变换为 -1），也可以用加法电路来处理减法。而数字的变号就用计算数字的二补数来完成。

在一般n位元的二进制数字中，最高有效位元（MSB）第 n位元代表的数字为 2n−1。不过，在n位元的二补数系统中，最高有效位元（MSB）第 n位元表示符号位元，若符号位元为0，数字为正数或0，若符号位元为1，数字为负数。以下是n位元的二补数系统中，几个特别的数字：

二补数｜实际数字｜附注

0 111....111｜2n−1-1｜当前有符号位区分的最大正数

...｜...

0 000....001｜1

0 000....000｜0

1 111....111｜-1

...｜...

1 000....001｜- 2n−1+1｜当n<当前所在二补数系统内所含的最大位元数量（最大位元数量是4的整数倍，且整数倍数大于等于1），得出根据当前n的值从而解得有符号位区分的负整数（同时根据n的值也确定了最大位元数量的值）。

1 000....000｜- 2n−1｜当n=当前所在二补数系统内所含的最大位元数量（最大位元数量是4的整数倍，且整数倍数大于等于1），得出根据当前n的值从而解得有符号位区分的最小负数（同时根据n的值也确定了最大位元数量的值）。

因此，在8位元的二补数系统中，可以表示的最大正数为28−1-1 = 127，可以表示最小的负数为 -28−1 = -128

### 计算二补数

在计算二进制数字的二补数时，会将数字进行位元反相运算，再将结果加1，不考虑溢位位元（一般情形，溢位位元会为0），就可以得到该数字的二补数。

以下考虑用有符号位8位元二进制表示的数字5：

0000 0101 (5)

其最高位元为0，因为此数字为正数。若要用二补数系统表示 -5，首先要将5的二进制进行反相运算〔1变为0，0变为1 〕：

1111 1010

目前的数字是数字5的一补数，因此需要再加1，才是二补数：

1111 1011 (-5)

以上就是在二补数系统中 -5的表示方式。其最高位元为1，因为此数字确实为负数。

一个负数的二补数就是其对应的正数。以 -5为例，先求数字的一补数：

0000 0100

再加一就是 -5的二补数，也就是5。

0000 0101 (5)

简单来说，数字a（正负数皆可）的二补数即为 -a。

若要计算n位数二补数二进制对应的十进制，需要知道每位数对应的数字，除了最高位元外，其他位元的对应数字均和一般二进制相同，即第i位数表示数字2i−1。但最高位元若为1时，其表示数字为 -2n−1，因此若用此方式计算0000 0101表示的数字，其结果为：

1111 1011 (−5) = −128 + 64 + 32 + 16 + 8 + 0 + 2 + 1 = (−27 + 26 + ...) = −5

### 特别的数字

有两个数字的二补数等于本身：一个是0，另一个为该位元内可表示有符号位区分的二进制形式的最大负数（即1000...）。

0的二补数计算方式（以8位元为例）如下：先计算它的一补数：

1111 1111

再将一补数加一：

0000 0000，溢位位元二进制值 = 1（二进制）

忽略溢位，其结果为0（0是唯一计算二补数过程中会出现溢位的数字。）。因此0的二补数为0。而0 x (-1) = 0，因此其二补数仍满足“数字a的二补数为 -a”的原则。

若计算1000 0000（这是8位元内可表示有符号位区分的二进制形式的最大负数-128）的二补数：先计算它的一补数：

0111 1111

再加一就是它的二补数。

1000 0000

1000 0000 (-128)的二补数仍为1000 0000 (-128)。但(-128) x (-1) = 128，因此其二补数是以上规则的例外。

总结：由于0可以等于0的二补数-0，以及同样因为8位元的二补数可显示的值范围为 -128 ~ 127，但-128的二补数128无法用在已有位元数量为8的位元数量内的可用二补数表示。【在计算其他位数内的可表示有符号位区分的二进制形式的最大负数（即1000...000）时，也会有类似情形。】

所以：0和-128的确是“数字a的二补数为 -a”原则中两个特别的数字。

### 其他计算方法

#### 方法一

另一种计算$-N$的二补数$N^*$ 的公式如下：

$$N^* = 2^n - N$$

其中$n$是整数的长度。

以八位元的“-123”（-11110112）求其二补数为例：

$$n = 8$$

$$N = +123 = 0111\ 1011_{(2)}$$

$$N^* = 2^n - N = {2^8} - 0111\ 1011_{(2)} = 1\ 0000\ 0000_{(2)} - 0111\ 1011_{(2)} = 1000\ 0101_{(2)}$$

$$-123_{(10)} = 1000\ 0101_{(2)}$$

$$-123=-128+5=1000\ 0000_{(2)} + 101_{(2)} = 1000\ 0101_{(2)}$$

#### 方法二

以另一种较简单的方式，可以找出二进制数字的二补数：

- 先由最低位元开始找。
- 若该位元为0，将二补数对应位元填0，继续找下一位元（较高的位元）。
- 若找到第一个为1的位元，将二补数对应位元填1。
- 将其余未转换的位元进行位元反相，将结果填入对应的二补数。

以0011 1100为例（图中的^表示目前转换的数字，-表示还不确定的位数）：

```
  原數字       补码
0011 1100  ---- ---0（此位元為0） 
        ^

0011 1100  ---- --00（此位元為0） 
       ^

0011 1100  ---- -100（找到第1個為1的位元） 
      ^

0011 1100  1100 0100（其余位元直接反相） 
^
```

因此其结果为1100 0100

### 符号延展

十进制｜4位元二补数｜8位元二补数

5｜0101｜0000 0101

-3｜1101｜1111 1101

将一个特定位元二补数系统的数字要以较多位元表示时（例如，将一个字节的变数复制到另一个二个字节），所有增加的高位元都要填入原数字的符号位元。在一些微处理机中，有指令可以执行上述的动作。若是没有，需要自行在程式中处理。==>

在二补数系统中，当数字要向右位移几个位元时，在位移后需将符号位元再填入原位置（算术移位），保持符号位元不变。以下是二个例子：

```
    數字      0010 1010             1010 1010
向右位移一次  0001 0101             1101 0101
向右位移二次  0000 1010             1110 1010
```

而当一个数字要向左位移n个位元时，最低位元填n个0，权值最高的n个位被抛弃。以下是二个例子：

```
    數字      0010 1010             1010 1010
向左位移一次  0101 0100             0101 0100
向左位移二次  1010 1000             1010 1000
```

向右位移一次相当于除以2，利用算术移位的方式可以确保位移后的数字正负号和原数字相同，因为一数字除以2后，不会改变其正负号。

注意：向左位移一次相当于乘以2，虽然乘以在理论上并不会改变一个数的符号，但是在二补数系统中，用以表示数的二进制码长度有限，能够表示的数的范围也是有限的：若一个数的高权值上的数位已经被占用，此时再将这个数左移若干位（乘以2n）的话，有可能造成数位溢出（overflow），高权值上的数将会失去，对于绝对值很大数，这将造成整体表达的错误。

## 二补数的工作原理

为什么二补数能这么巧妙实现了正负数的加减运算？答案是：指定n位元字长，那么就只有2n个可能的值，加减法运算都存在上溢出与下溢出的情况，实际上都等价于模2n的加减法运算。这对于n位元无符号整数类型或是n位元有符号整数类型都同样适用。

例如，8位元无符号整数的值的范围是0到255.因此4+254将上溢出，结果是2，即$(4+254) \equiv 258 \equiv 2 \pmod{256}$。

例如，8位元有符号整数的值的范围，如果规定为−128到127，则126+125将上溢出，结果是−5，即$(126+125) \equiv 251 \equiv -5 \pmod{256}$。

对于8位元字长的有符号整数类型，以28即256为模，则

$$\begin{align} -128 & \equiv 128 \pmod{256} \\ -127 & \equiv 129 \pmod{256} \\ \vdots \\ -2 & \equiv 254 \pmod{256} \\ -1 & \equiv 255 \pmod{256} \\ \end{align}$$

所以模256下的加减法，用0, 1, 2,..., 254,255表示其值，或者用−128, −127,..., −1, 0, 1, 2,...,127是完全等价的。−128与128，−127与129，...，−2与254，−1与255可以互换而加减法的结果不变。从而，把8位元（octet）的高半部分（即二进制的1000 0000到1111 1111）解释为−128到−1，同样也实现了模256的加减法，而且所需要的CPU加法运算器的电路实现与8位元无符号整数并无不同。

实际上对于8比特的存储单元，把它的取值00000000,..., 11111111解释为0, 255，或者-1, 254，或者-2, 253，或者-128, 127，或者-200, 55，甚至或者500, 755，对于加法硬件实现并无不同。

## 运算

### 加法

二补数系统数字的加法和一般加法相同，而且在运算完成后就可以看出结果的正负号，不需特别的处理。

正数与负数相加不会出现上溢错误，因为它们的和一定会小于加数。上溢错误只有在两个正数或两个负数相加时才可能发生，这时候最高有效位（正负号）会变成相反。

以15加-5为例：

```
 11111 111（进位）
  0000 1111 (15)
+ 1111 1011 (-5)

  0000 1010 (10)
```

由于加数和被加数都是8位元，因此运算结果也限制在8位元内。第8位元相加后产生的进位不考虑（因为不存在第9位元）的1被忽略，所以其结果为10。而15 + (-5) = 10，计算结果正确。

在以上计算式中，可以由进位列的最左侧二个位元得知结果是否出现溢位。溢位就是数字的绝对值太大，以致于无法在指定的二进制位元个数来表示（在此例中，是超过8位元的范围）。若进位列的最左侧二个位元同为0或同为1，表示结果正确，若是一个为0，另一个为1，表示出现溢位错误。也可以对此二个位元进行异或运算，结果为1时，表示出现溢位错误。以下以7 + 3的4位元加法说明溢位错误的情形。

```
 0111（进位）
  0111 (7)
+ 0011 (3)

  1010 (−6)  結果不正確！
```

在此例中，进位列的最左侧二个位元为01，因此出现溢位错误。溢位的原因是7 + 3的结果(10)超过二补数系统4位元所可以表示的数字范围 -8~7。

故为防止溢位错误，二补数在进行加法运算时通常将符号位进行复制后追加到最高位之前，即设二补数B的位数为WIDTH，则B′={BWIDTH-1,B}。应注意此处B′的位数为WIDTH+1。

如上两例用此方法进行计算：

```
  11 1111 111（进位）
   0 0000 1111 (15)
  +1 1111 1011 (-5)

(1)0 0000 1010 (10)
```

由于WIDTH+1=8+1=9，故而第十位的1同样由于溢位而被省略，结果仍为10。两负数（符号位为1）相加时同理。

```
    111（进位）
  0 0111 (7)
+ 0 0011 (3)

  0 1010 (10)  結果正確！
```

由于WIDTH+1=4+1=5，故第五位的0仍为符号位，得结果正数10（十进制）。

### 减法

减法通常转化为加法进行运算，将减数与被减数的补码进行加法运算，即可得出差。

### 乘法

乘法在电脑的世界里其实就是不断的做加法。

例如:3*5=3+3+3+3+3=15

### 除法

除法就是相减。

例如：10/3=10-3-3-3=1mod3 而减法又可做二补数相加，所以所有四则运算的基础都是由加法而来。