关于补码的由来和做用

最近在读《深刻理解计算机系统》(CSAPP),第二章中关于补码的描述颇有意思,在书中并无详细叙述补码的由来和为何要使用补码来表示有符号数,而不是用原码和反码。相反这本书详细的叙述了补码的数学表示,以及公式的推导!对补码的由来却一笔带过,甚至原码和反码只是简单的在后面的篮框提示中提了一下,根本没有出如今正文。性能

这在必定程度上形成了个人阅读困难,因而在搜索引擎的帮助下,我查了不少资料,了解到补码的更多细节,以及这个神奇的编码方式如何帮助人们设计CPU时节省大量的精力和金钱。这里我把他记录下来。搜索引擎

计算机中的信息都是用二进制表示的,在表示无符号数时并无什么问题,可是在表示有符号整数时就出现了问题,如何在二进制中区分正数与负数呢?
区分正数和负数的另外一个意义在于若是可以准确的表示出、负数,咱们就能够化减法为加法,如5-4就能够表示为5+(-4),至于为何那么多先辈要执着于把减法化为加法,缘由很简单,对计算机的电路而言,计算加法要比计算减法方便的多。
不少人提出了有符号整数的编码方式,其中有:原码(Sign-Magnitude)、反码(ones' Complement)、补码(two's Complement)。编码

原码

原码的英文为sign-magnitude,第一个单词为符号的意思,第二个单词通过查询有“大小”的意思,大意应该为符号-数值的组合,在原码中,第一个数字表明符号位,1表明负数,0表明正数,余下的为数值位,用来表示具体的数值。这种编码方式最简洁易懂,也最符合人的思惟。好比:-5用4位二进制原码来表示就是1101。
CSAPP中给出的原码数学公式为:
TIM截图20191105192713.png
由此咱们能推断出SMAXw=2w-1-1,SMINw=-(2w-1-1)。如八位二进制原码的范围应该为[-127,127]。
使用原码来让机器作运算彷佛是可行的,可是实际上,这种方法对机器来讲倒是十分困难的,机器没法像人同样判断出0是+,1是-,设计出一个能让机器判断正负的电路也是十分复杂和困难的。
实际上目前咱们不多用原码来作运算,原码目前经常用来做为咱们求补码的一个过渡。翻译

反码

反码的英文为ones'complement,乍看之下有些迷惑,翻译过来是“不少个1的补”,彷佛更迷惑了。。。
其实反码确实能够用“不少1的补”来理解,咱们都知道w位二进制数表示的最大范围是2w-1,用位向量来表示就是[111111....11111],就是不少个1。假设x是正数,求-x的补码的含义实际上就是求[11111...111]-x,也就是(2w-1-x)(其中w为位数),这是反码的一种计算方式,另外一种计算方式就是用原码计算,原码的符号位不变,剩下的数值位所有取反,好比1011的反码就位1100。这和上面的计算方法获得的结果是一致的。
固然要注意的是上面所说的计算方法只对负整数有做用,正整数的反码实际上和正常的无符号数编码没有区别。
反码的另外一种具体数学解释是在CSAPP中看到的:
TIM截图20191105192723.png
由此咱们能推断出OMAXw=2w-1-1,OMINw=-(2w-1-1)。如八位二进制原码的范围应该为[-127,127]。
这个公式应该能够经过上面的提到的计算方法严格证实出来。使用此公式能够快速的算出反码表示的值。
反码的优势是计算快捷,不只仅是对人而言,计算一个负数的补码,只须要将其的正数表示按位取反便可,对于机器来讲,取反可比作加减法快多了。
反码实际上能够用来作二进制运算,它的规则是从低位到高位逐列进行计算。1和1相加是0但要产生一个进位1,0和1相加是1,0和0相加是0。若最高位相加后产生进位,则最后获得的结果要加1。(反码运算的原理将在下面的补码运算中解释)。
须要注意的是与原码不一样的是,反码的运算符号位与数值一块儿参加运算。
用反码运算,其运算结果亦为反码。在转换为真值时,若符号位为0,数位不变;若符号位为1,应将结果求反才是其真值。设计

原码和反码的缺陷

原码和反码在不考虑性能的状况下确实可以用来作二进制有符号整数的计算,历史上也确有基于这两种编码方式的机器。可是这两种编码却存在不少难以解决的缺陷。
考虑四位二进制数[0000],在原码中表示0,在反码中也表示0,而对于原码来讲[1000]也表示0,对于反码来讲[1111]也表示0。通常咱们把[0000]称为+0,而另外一种表示方法称为-0。
一个数字有两种编码方式,这显然是不合理的!
为了解决这种不合理咱们引入了补码的概念,它继承了反码易于计算的优势,也解决了原码难以计算的问题。指针


介绍完了原码反码,在介绍补码以前,为了便于理解,咱们首先要介绍一下模的概念:blog

“模”是指一个计量系统的计数范围。如时钟等。计算机也能够当作一个计量机器,它也有一个计量范围,即都存在一个“模”。
例如:
时钟的计量范围是1~12,模=12。表示n位的计算机计量范围是0~2^(n) -1,模=2^(n)。
“模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。任何有模的计量器,都可化减法为加法运算。
在计算机中,计算加法要比计算减法要容易和便宜,那么到底如何在计量装置中用加法代替减法呢?
这里咱们能够用时钟举一个例子:
TIM截图20191104215527.png
这是一个只有时针的时钟,目前它指向9。若是咱们想让他回退到4。咱们有什么办法呢?
最简单的方法是,倒退五个单位,9-5=4。可是这里咱们不想让指针回退,是否有另外的方法可以让指针指向4呢?
答案是把指针向前拨7个单位。
TIM截图20191104220151.png
为何可以这么作呢,这就是前面咱们提到的“模”的做用。
9+7=16=12+4,在时钟这个计量机器上进位并不能显示出来,因此最后时钟上显示的是4。
在以12模的系统中,加7和减5效果是同样的,所以凡是减5运算,均可以用加7来代替。对“模”而言,8和4互为补数。实际上以12模的系统中,11和1,10和2,9和3,8和4,6和6都有这个特性。共同的特色是二者相加等于模。
一样的概念在计算机中也彻底成立,考虑一个四位的二进制数,它的范围为[0,255],模为256,把这个概念扩展一下。w位的二进制数的模就为2w
模就是补码之因此可以进行二进制计算的基本原理。继承


补码

如今咱们来谈谈本文的主角补码(Two's complement),它的英文名依旧很迷惑,不过不要紧,下面我会用引入模的概念来帮助咱们理解补码的具体含义。
咱们知道一个模为10的计算系统中,-4与+6的结果是相同的(9+6=10+5=15,舍去进位最后的结果为5与9-4相同),咱们把这个概念扩展:
在一个模为M的系统中,咱们须要计算m-n,而-n在模为M的系统中能够转化为+(M-n),因此只须要计算m+(M-n),这样就成功的把减法转换成了加法。
把其扩展到二进制中:
在一个位数为w的计算系统中,咱们须要计算m-n(n为正整数),而-n在系统中能够转化为(2w-n),因此只须要计算m+(2w-n)并把溢出的位舍去,而-n的补码计算方法就是为2w-n。这也是英文名为Two's complement的缘由,意为2的补,这里只有一个2,因此用Two's complement。
补码彷佛已经十分完美了,它不只解决了0的编码重复问题(在补码中0只有[0000]一种表示),也成功的把减法变成了加法,可是问题来了,为了把减法变成加法而引入了减法不是像一个笑话吗(手动狗头)。
这个时候咱们就须要用到原码和反码了,咱们来仔细观察一下反码的计算方法,对一个负整数位数为w的二进制数-n来讲,它的反码表示为(2w-1-n),对比补码的表示2w-n,咱们可以发现补码=反码+1。
咱们很快就能推出对于一个负整数来讲,咱们能够算出它的二进制相反数表示,而后对全部的位取反,最后加一,获得的就是补码的值。这样咱们就使用反码成功解决了减法的问题。
固然了,和反码同样,上面的表示都是对负整数而言,正整数的无符号码、原码、反码、补码都是同样的~
CSAPP给出了补码的数学公式:
TIM截图20191105192735.png
一样的这个公式咱们也能够经过证实获得,而且经过这个公式咱们也能够直接计算出补码的实际值。
由此咱们能推断出由此咱们能推断出TMAXw=2w-1-1,TMINw=-2w-1。如八位二进制原码的范围应该为[-128,127]。这也是为何在通常的程序设计语言中,无符号数据类型的负数范围要比正数大了。
补码的计算就十分简单了,直接相加就能够获得答案了~(固然溢出进位依然是要舍去的。),这也是反码的计算原理。索引

一点感想

补码的出现解决了不少问题,出于崇敬我上网搜索了补码的发明者,结果是。。。。没有搜索到,而且通过查证,早在1645年,Pascal的计算器用的十进制补码。后来到了20世纪,经由冯诺依曼引入到了计算机中。
经典的理论与知识老是在流传中越发让人感到先辈们的伟大,想必3个世纪以前的那位大师在使用补码来解决计算问题时并无想到有一天信息时代的崛起会让他的发现即便到了今天也依旧熠熠生辉。数学

相关文章
相关标签/搜索