VIRTUALS

the virtual labs for the virtuals

0%

浅谈原码、反码和补码

摘要:
计算机二进制原码、反码和补码的个人理解。

首先应该明确一点,原码、反码和补码,它是一套由伟大的计算机前辈们设计出来的系统。目的是适配计算机的正负数表示,和加减运算。
当我认真理解了原码、反码和补码这一套系统之后,我深深地觉得如果自己出生于恰当的年代而且恰好也是一位计算机科学家的话,我大概也会这样设计,因为它是那么的和谐且自然————仿佛本就该如此。

原码

一个int型整数的二进制表示为 $32$ 个 $0$ 或 $1$,比如 $10$,它的二进制表示为 $1010_2$(前方的 $0$ 省略了)。计算机被设计出来的时候,自带了一个加法器的东西。于是,整数的加法运算很简单。例如,$10 + 1$,它的二进制计算过程是:

0000 0000 0000 0000 0000 0000 0000 1010
+
0000 0000 0000 0000 0000 0000 0000 0001
=
0000 0000 0000 0000 0000 0000 0000 1011

$-10$的原码二进制表示为:

1000 0000 0000 0000 0000 0000 0000 1010
这就是原码的定义,最高位是 $0$,表示正数;最高位是 $1$ 时,表示负数。其他位表示该数绝对值对应的二进制位。

两个正数可以直接按照原码进行加法运算,那么减法呢?
当进行减法运算时,我们必须考虑用负数代替减法,把减法转化为加法运算。但是直接拿原码进行减法运算显然得出错误结论(即得到的结果值按照原码的表示方法和正确数值不对应)。为了表示负数,和将负数用于加法计算,科学家设计出了反码表示。

反码的由来

由于需要表示负数,科学家设计了反码。它的设计原则是:
x + (-x) = 0
即,一个数的负数加上它本身等于 $0$。
看一下这个例子:
数字 $10$ 的负数应该怎么表示?
$10$ 的原码二进制表示为:

0000 0000 0000 0000 0000 0000 0000 1010

$-10$ 的原码二进制表示为:

1000 0000 0000 0000 0000 0000 0000 1010

当我们运算 $10 + (-10)$ 时,如果直接拿原码当负数进行加法运算,得到的值是:

1000 0000 0000 0000 0000 0000 0000 1100

显然是错的,它并不是 $0$。
于是科学家为了适配负数计算,将负数表示为:
原码最高位保持不变,其他位每位取反。
这就是反码。
那么,$10 + (-10)$ 就变成了:

0000 0000 0000 0000 0000 0000 0000 1010
+
1111 1111 1111 1111 1111 1111 1111 0101
=
1111 1111 1111 1111 1111 1111 1111 1111
注意,负数参与的加法得到的值依然是反码。为什么呢?因为:
正数的反码就是其原码
两个反码相加,结果当然还表示为反码。这里就很清晰了,上述计算结果最高位 $1$ 表示负数,其他位取反还原成原码就是:

1000 0000 0000 0000 0000 0000 0000 0000

表示成 $-0$。
那么,你可以注意到 $-0$ 的反码表示为:

1111 1111 1111 1111 1111 1111 1111 1111

而 $+0$ 的反码表示为:

0000 0000 0000 0000 0000 0000 0000 0000

而 $+0 = -0 = 0$

当科学家设计到这里的时候,甭提多尴尬了,同一个数居然出现了两种表示方式。于是,闪耀着人类智慧光芒的 补码 应运而生。

补码

为了让 $0$ 只有一种表示方式,科学家们大腿一拍,决定:

先让0表示为0000 0000 0000 0000 0000 0000 0000 0000
所有正数原地不动,表示为其原码。
再让所有负数向多出来的-0位置移动一位。即所有负数表示为反码+1位偏移

这就是补码。
为了更好地理解这一块,你可以把 $0$ 附近的数在计算机中看成是这样排列的:

-3 -2 -1 -0 +0 +1 +2 +3
按照反码规则,他们确实是这样排列的。

做个总结:

  • 原码:正数不变,负数最高位取 $1$ 表示符号,其余位保持和对应的正数相同(给人看的,让你一看就知道是负几)。
  • 反码:正数和原码相同,负数最高位取 $1$,其余位各位取反(给计算机看的,让计算机可以计算负数加法)。
  • 补码:正数和原码相同,负数为原码取反再加 $1$(给计算机看的,让计算机可以处理 $0$)。
    其中,你如果直接看负数的补码会觉得好奇怪,其实这个只是一种表示方法,一种外在的表现形式。你按照计算机的规则先减 $1$ 再取反就得到这个负数的原码的。你就能看懂了。

举个例子:
$3 + (-5)$

$3$ 的补码为:

0000 0000 0000 0000 0000 0000 0000 0011

$-5$ 的原码为:

1000 0000 0000 0000 0000 0000 0000 0101

反码为:

1111 1111 1111 1111 1111 1111 1111 1010

补码为:

1111 1111 1111 1111 1111 1111 1111 1011

$3 + (-5)$ 计算机的计算过程是这样的:

0000 0000 0000 0000 0000 0000 0000 0011
+
1111 1111 1111 1111 1111 1111 1111 1011
=
1111 1111 1111 1111 1111 1111 1111 1110
结果的补码看起来好奇怪,但是记住这是给计算机看的,在补码体系下它就是被设计成用这一串奇奇怪怪的 $1$、$0$ 表示 $-2$。我们只需将其减 $1$ 再取反即可得到结果的原码表示:

结果减 $1$ 当然也是加上 $1$的补码:

1111 1111 1111 1111 1111 1111 1111 1110 + //结果补码
1111 1111 1111 1111 1111 1111 1111 1111 = //-1补码
1111 1111 1111 1111 1111 1111 1111 1101 //临时码

高位不变其他位取反:
1000 0000 0000 0000 0000 0000 0000 0010 //结果原码

一眼就看出这是 $-2$.
其实所谓的一眼看出,不过是让最高位符号匹配后面的正数。

参考: