补码、反码,傻傻分不清?

补码

补码的意义在于表达负数,进而将相对麻烦的减法运算转换为计算机擅长的加法。

我们先来看看,如果单纯用「符号位+原码」的表示方法,会出现什么幺蛾子,比如符号位0表示正、1表示负:

正数二进制原码负数二进制原码
00 0000-01 0000
10 0001-11 0001
20 0010-21 0010
30 0011-31 0011
40 0100-41 0100

当进行两个正数的相加,比如1+2=3:
\begin{matrix} & 0\ 0001 \\ + & 0\ 0010 \\ \hline & 0\ 0011 \end{matrix}

很OK。

两个负数呢?
\begin{matrix} & 1\ 0001 \\ + & 1\ 0010 \\ \hline & 0\ 0011 \end{matrix}

除了符号位由于进位出了差错,绝对值上是没毛病的,只要在运算前单独识别一下正负,不让符号位参与运算就行了。

然而,当正数与负数相加,比如1+(-1),也就是两个正数相减1-1,结果会是我们所期望的0吗?
\begin{matrix} & 0\ 0001 \\ + & 1\ 0001 \\ \hline & 1\ 0010 \end{matrix}

哦不,它不是,它是莫名其妙的-2。

于是就轮到了补码登场。

时钟就是生活中一个用到补码思想的活生生的例子,在我们定时(比如定闹钟)的时候,如果要将原本的6点调整到9点,可以选择朝前拨3个小时,也可以选择朝后拨9个小时。因为在不考虑进位的情况下,6+3=6-9。这意味着,3和-9有着同等的效力,因为在时钟的十二进制中,3就是-9的补码。同理,-8的补码是4,-7的补码是5,等等。规律是,两者的绝对值之和为12。

推广到二进制上,-1的补码是1(两者绝对值之和为2),-01的补码是11(两者绝对值之和为4),-001的补码是111(两者绝对值之和为8)……
总结而言:n位二进制负数a的补码为:
2^n-|a|

这个公式已经清晰明了,如果用十进制来进一步理解,就变得更加简单。比如你买了3块钱的东西,给老板一张10元纸币,老板就需要找你7元。如果你给老板一张100元的纸币,老板就要找你97元。这个找头,就是补码。

于是,对于4位二进制数,有:(正数还是写作原码)

正数二进制补码负数二进制补码
00000-00000
10001-11111
20010-21110
30011-31101
40100-41100

我们不再需要符号位,并且正负数的相加运算就变得完美无瑕,比如1+(-2):
\begin{matrix} & 0001 \\ + & 1110 \\ \hline & 1111 \end{matrix}

原来补码就是这么一个简单的概念,那为什么印象里总觉得十分混乱呢?罪魁祸首就是该死的反码。

反码

反码就是对原码的每一位进行取反,即n位二进制负数a的反码为:
\begin{matrix} \underbrace{ 11\cdots1 } & -\ |a| \\ n个1 \end{matrix}

n位十进制负数a的反码为:
\begin{matrix} \underbrace{ 99\cdots9 } & -\ |a| \\ n个9 \end{matrix}

这个概念其实很没必要,它存在的唯一意义就是方便心算补码。还记得那句宛如绕口令般的「口诀」吗?

补码等于原码的反码加1

这个「加1」是怎么来的,且看:
\begin{cases} a的补码 = 2^n - |a| \\ a的反码 = (2^n-1) - |a| \end{cases}

这就好比你买了3块钱的东西,却非要付99元……

尽管反码能帮助初学者快速心算补码,却也容易给我们的长期记忆造成混乱。我打赌,长久不碰之后,你甚至一时半会想不起来反码和补码的区别。

英文中的补码

其实在英文中,反码也叫补码。

30022-56a4f23321563fa0.jpeg

但两者的名称反而更容易让人理解。比如对于二进制,补码叫做「two's complement」,反码叫做「ones' complement」。注意那一撇的位置,前者可以理解为「二进制的补码」,后者可以理解为「一堆1的补码」,或简称「补1码」。

对于十进制,补码就是「ten's complement」,反码就是「nines' complement」(补9码)。

「complement」就是「补充、补足」的意思,它强调的是「补」,翻译为「补码」再合适不过,「反码」看似讨巧,却徒增了我们的理解负担。

所以忘掉反码吧,就把补码留下。

补码算法溯源

下面以对人友好的十进制为例,介绍两种使用补码将减法转换为加法的方法。比如要算:
\begin{matrix} & 512 \\ - & 128 \\ \hline & 384 \end{matrix}

第一种方法是利用被减数的补9码:

  1. 计算被减数的补9码:999 - 512 = 487
  2. 将被减数的补9码与减数相加:487 + 128 = 615
  3. 计算上一步结果的补9码,即最终结果:999 - 615 = 384

该方法的本质是:
a - b = 999 - [(999 - a) + b]

第二种方法是利用减数的补9码:

  1. 计算减数的补9码:999 - 128 = 871
  2. 将被减数与减数的补9码相加:512 + 871 = 1383
  3. 丢弃上一步结果最高位上的1:1383 - 1000 = 383
  4. 将上一步结果加1,即最终结果:383 + 1 = 384

该方法的本质是:
a - b = a + (999-b) - 1000 + 1

也等价于直接利用减数的补码:
a - b = a + (1000-b) - 1000

作为计算机补码运算的开山鼻祖,帕斯卡的算术机中用的是第一种方法,而如今的计算机用的是第二种方法。

参考资料


2019年11月8日 无锡

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值