补码的起源

补码的起源

在介绍之前,我们先来解决两个小问题:

  • 假设你现在拥有一台计算机,只有若干个4位的寄存器,如何用它来计算10+3 ?

  为了让机器计算,我们需要把数字转化成二进制交给计算机。于是,我们先把10写成1010,3写成0011,然后分别存入4位的寄存器,交给加法器进行加法操作得到10+3=1010+0011=1101=13。好,第一个问题解决了。

  • 还是有若干个4位的寄存器,如何用它计算10-3 呢?

  我们都知道,计算机内实现减法的电路比加法复杂很多,因此我们需要考虑的是,如何让只做加法的机器做减法算术呢?

  其实,解决这个问题后,补码的起源就真相大白了。


 

  如何用加法实现减法呢?先人早已从生活中找到了解决办法:钟。

  我们都知道,钟总是顺时针移动着。如果我们把顺时针看作做加法,逆时针看作做减法,我们可以发现,钟也是一种只能做加法的机器。尽管如此,人们发现钟是可以用减法实现加法的。

  假设,当前时间是10:00,我们想知道7小时前是几点?

  用人脑可以很快反应出这是个减法操作:10-7,我们只要逆时针数7大格,于是可以得出10-7=3。那如何用加法实现呢?其实也很简单:逆时针转7格就等价于顺时针转12-7=5格——10+5=15,由于是开始了第二轮了,所以钟面显示的值自然是15-12=3。

  为了科学严谨的描述这样一个有趣的现象,人们把钟一圈的12个小时定义为大小(也就是说,模就等于一个机器最多能表示的数字个数)。于是,在钟算中,-x等价于+(-x)。因此,当我们想计算10-7,“钟算”中,-7等价于+(12-7)=5小时,最终得到10-7=10+5=3点。

  再举个例子,当我们想运算 11-8,同理可得“钟算”中,-8等价于+(12-8)=4小时,最终得到11-8=11+4=3点。

  看到这里,你也许会觉得钟算可以非常完美的解决减法问题了。但是很快,人们发现钟算用减法代替加法是有局限的:“钟算”只能用来做大数-小数的减法。假如我们现在要计算7-10,那么按照“钟算”来,我们将得到7+(12-10)=9,然而实际上却应该等于-3!那该怎么办呢?

  仔细思考一下,我们很容易会发现,其实7-10肯定是不能用钟算实现的,因为钟面上根本没有-3的数字!既然这样,那只有一个办法:我们将钟面上的9当成-3。这样一来,“钟算”就没有问题啦~

  那么,除了把9看作-3,还有哪些数字需要看作负数呢?

  为了让“钟算”中,负数和正数的算术能力均衡,我们就一半一半的分开表示:0-5这6个数字表示本身的数,6-11这6个数字对应表示-6~-1,这样我们就发现其中9正好对应的是-3.

  于是,生活中的“钟”,经过我们的改造,变成了真正可以用“加法”实现“减法”的钟了,也就是说,改造后的“钟”,利用“钟算”的规则,小数-大数的减法也可以正确实现了。

  现在,我们再用一个例子来验证一下:计算4-9,利用“钟算”转化成加法即,4+(12-9)=7,而钟面7的的位置在我们的新钟中已经变成了-5,计算正确。

  不过值得注意的是,利用原来的钟,加法计算结果的最大值是11,现在最大只能是5了,也就说,用这个改造后的钟,虽然可以计算减法了,但是代价是能做的加法变少了,计算结果的范围从0~11变成了-6~5,不过可以表示的总个数都是12个没有变,也就是模的大小。举个简单的例子来更清晰的说明这个代价:原先的钟可以用来计算3+4=7,而现在无法计算此算式了,因为7已经超出了最大正数5的范围了;但是它可以用来做小数-大数的减法了,如3-5=3+(12-5)=-2.

                 


 

  好,现在让我们回到计算机的问题中来,来解决如何让计算机计算10-3的问题。

  参考钟的实例,类比不难得到4位寄存器的计算机,模就是16(参考前面模的解释)

  为了让这16个数字也要实现像上面“钟算”的功能,我们要构造一个圈循环(这其实就是补码的本质)。其实很简单,我们可以完全模仿。由于模是16,所以初始圈的数字是0~15,所以我们只需要改造成-8~7(具体对应细节见下图)。这样一来,我们就可以采用和刚刚钟算一样的方法,用加法实现减法了。

                    

  将上面的改造结果列成表格:

钟的初始值(D)

对应的二进制

改造后对应的值(D)

0

0000

0

1

0001

1

2

0010

2

3

0011

3

4

0100

4

5

0101

5

6

0110

6

7

0111

7

8

1000

-8

9

1001

-7

10

1010

-6

11

1011

-5

12

1100

-4

13

1101

-3

14

1110

-2

15

1111

-1

 

  钟的初始值和二进制的对应关系我们叫做原码,那么改造后的值和中间二进制的对应关系该怎么命名呢?于是,人们就叫它补码

  为什么这么叫呢?因为我们很容易发现,改造后的负数的绝对值+对应的改造前的正数=。所以我们可以说,-5的补码是11的原码1100;-4的补码就是12的原码1101……

  简单总结一下补码的起源:将寄存器的所能表示的所有的数字仿照钟排列成循环圈,然后对钟面的值进行改造,使得可以用减法来实现加法。

  到此,补码的由来其实已经全部结束了!


 

 

现在我们解答很多同学对于补码的疑问:

  ①补码中,我们说,第一位是符号位,1表示负,0表示正,是人为定义的吗?

  显然,不是人为定义的。回顾一下,其实是因为我们要让钟算完成小数-大数的减法运算,不得不将其中某些通过钟算计算得到的正数看作是负值,例如最开始提及的4-9的例子中,我们用钟算可以得到结果是4+(12-9)=7,于是我们将钟面上的7看作是正确的结果-5。然后,我们为了让可以计算的正数结果和负数结果相同,所以将其中一半这样的值都看作了对应正确的负值。

  神奇的地方在于,当我们被迫这么做后,我们可以观察上面的表格,惊喜的发现,所有的负数对应的补码最高位都是1,而所有的正数最高为都是0.

  ②补码中,正数的补码等于原码,负数的补码由原码得到:符号位为1,其余位原码取反加一。为什么要这么做?

  正数的补码从上面的表格中已经可以看到的确就是原码,负数的补码我们先用这个规则验证一下:

  例如求-5的补码:先写出-5的原码是1101,然后补码的最高位为1不变,其余位是101取反加一,也就是010+1=011。因此,-5的补码就是1011,查上表发现的确正确。

  那么,这个巧妙的规则是如何发现的呢?我们以计算16-5为例子进行说明:

  首先我们把16写成二进制,即1 0000,我们需要计算16-5,也就是 10000-0101=(1111-0101)+1=1010+1=1011。现在你终于明白了吧?1111减去0101就相当于对0101取反,然后再加一即可

  一句话总接一下吧:当人们想使用一种只能做加法的机器来解决小数-大数的减法问题,并且希望能表示的负数和非负数平分时,补码和数字的对应关系就已经全部确定了。而后,人们从这些对应关系中总结出了很多有意思的规律,也就是我们的老师们在课堂上讲授的各种补码规则,它们可以帮助我们很快速的写出任何一个数的补码。因此,老师会选择将这个非常巧妙的结论直接告诉了我们,希望我们可以记住,(当然,副作用是造成了很多人觉得这些人为规定空穴来风)。如果今天你仔细看了这篇文章,相信对其中很多的规则一定更加清楚了吧!

posted @ 2018-04-09 13:11  ExPuzzle  阅读(560)  评论(0)    收藏  举报