美文网首页
补码、反码,傻傻分不清?

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

作者: 逸之 | 来源:发表于2019-11-09 11:04 被阅读0次

补码

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

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

正数 二进制原码 负数 二进制原码
0 0 0000 -0 1 0000
1 0 0001 -1 1 0001
2 0 0010 -2 1 0010
3 0 0011 -3 1 0011
4 0 0100 -4 1 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位二进制数,有:(正数还是写作原码)

正数 二进制补码 负数 二进制补码
0 0000 -0 0000
1 0001 -1 1111
2 0010 -2 1110
3 0011 -3 1101
4 0100 -4 1100

我们不再需要符号位,并且正负数的相加运算就变得完美无瑕,比如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元……

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

英文中的补码

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

但两者的名称反而更容易让人理解。比如对于二进制,补码叫做「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日 无锡

相关文章

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

    补码 补码的意义在于表达负数,进而将相对麻烦的减法运算转换为计算机擅长的加法。 我们先来看看,如果单纯用「符号位+...

  • 补码、反码、原码、位运算傻傻分不清?

    推荐阅读:1. 挑战10个最难回答的Java面试题,你能答对几个? 2. 秋招Java岗:最全的BA...

  • 2018-10-22 Python31 原码、反码、补码

    原码、反码、补码 1)如何计算补码?规则: 正数:原码 = 反码 = 补码负数:反码 = 符号位不变,其他位取反补...

  • 原码、反码、补码和移码

    书中关于原码、反码、补码和移码的定义如下(n是机器字长):原码: 反码: 补码: 移码: 原码, 反码, 补码的基...

  • 补码

    原码、反码、补码 原码:符号位 反码:减法变加法 补码:+1解决-0问题 数学原理 同余 反码:mod127 补码...

  • 软件测评师做题技巧

    第一类补码,反码,原码 正数:原码=反码;补码=原码负数:反码=原码求反;补码=反码+1 第二类用例概率的计算 测...

  • 计算机为什么要使用原码、反码、补码

    1. 什么是原码、反码、补码 先看一个例子: +1和-1的原码、反码、补码的表示 原码、反码、补码的计算方式如下:...

  • 数据的表示

    1位符号位+7位数值位正数:原码=反码=补码负数:反码=原码数值位取反 符号位不变补码=反码+1补码的表示范围位数...

  • 进制关系

    各进制的形式 正数的原码、反码、补码相同 负数的反码为原码各位取反,补码为反码+1 计算机的底层都是以补码的方式来...

  • JAVA中负数的二进制表示

    计算机对有符号数(包括浮点数)的表示有三种方法:原码、反码和补码,反码=原码按位取反 补码=反码+1。 移码为补码...

网友评论

      本文标题:补码、反码,傻傻分不清?

      本文链接:https://www.haomeiwen.com/subject/irpwbctx.html