您的位置:首页 > 理论基础

关于原码,反码,补码的一点注记

2017-09-02 09:08 183 查看
整数在计算机中是通过二进制的比特串来存储的。

将整数编码成二进制比特表示有几种方法,即通常所说的原码,反码补码

比如我们用4位模式xxxx,其中x∈{0,1}来存储一个整数,那么第一位是被预留表示整数的符号的,0代表正数,而1代表负数。比如-3就是1011;2就是0010

除了这个第一位的表示,剩余的位就是正常的二进制翻译,这种表示称之为原码

原码的一个缺点在于计算的时候会出现问题,特别是整数的减法。最简单的比如0−1,如果用原码表示,直觉上似乎是0000-0001=1111,这样转化成原码就是0-1=-7了,显然不正确;或者我们不知道应该怎么算。再比如1+(-1)=0001+1001=1010=-2?所以,原码的计算是有问题的。

事实上,在计算机之中,我们不需要考虑减法,而只考虑加法,当两个数相减的时候,我们考虑负数。另一方面,这样做需要配合上补码的表示,便可以形成完美的无差错运算体系。那么,究竟什么是补码?

一般地,对于一个整数z,它的补码有两种人工的求法,当然他们的结果是一样的。如果它是正数,则其补码就等于它的原码;如果它是负数,那么它的补码是这样定义的,将其绝对值对应的原码(即-x的原码)的位模式从最右数起,碰见的第一个1不动,而将这个1左边的位模式全部取反(即0变成1,1变成0)。如图1.



另一种方法,就是通过反码。简单来说,所谓反码就是按位取反。对于正整数,反码和原码一样,而对于负数,它的反码等于将原码除符号位以外的位全部取反。这样,第二种求补码的方式就是,正整数和原码一样,而负整数而言,是将它的反码+1(即通常的二进制加1)。

最自然的问题,就是为什么这样定义?这样定义为什么合理?并且使之恰好成为完美的运算体系?

首先我们注意到,一套位模式(简单起见我们考虑短的位模式作分析,长度不是本质的问题,对于长的模式也是一样的道理),比如说4位xxxx,那么它所能表示的整数有一个范围。当第一位由于计算机的原因必须用来表示符号以外,这个位模式能表示的最大正数显然是0111,也就是7. 那负数最小应该是1111,也就是-7(如图2). 这样的表示方式有一个天然的缺陷,即1000没法表示任何东西,因为-0显然没有必要。我们先不管这个问题,先看一个更重要的问题,就是在计算机之中正常的加法也“不正常”,也就是所谓的溢出。当我们仅仅能用4位模式(或者一般地,计算机中就是有限位模式,不论多大都是有限)来存储数据的时候,我们的存储范围是有限的,即我们不可能存储所有的整数。因此加法的封闭性是无法保证的。(而也是没有必要保证的,因为我们采用的是正常的整数加法,溢出了就是工程上需要考虑的问题。另外,如果要保证加法的封闭性,我们对于这个有限集合上的加法就需要重新定义了,即通过“模加法”,它其实也可以通过同态来得到。但是这样就不是正常的加法运算了,而我们在求两个整数加法的时候是要常规的加。)



这个时候,我们需要确定这个有限集中的每个元素对应的位模式表示。我们要做的,是要使得位模式表示既能够一一对应这个范围内的整数,又保持运算。从代数上说,这差一点就是就是同态。之所以说差一点,是因为溢出的存在,使得这两个集合的加法运算都不是封闭的。

于是我们考虑更一般的情形,所有的整数集合Z和它的二进制表达B之间是一一对应的(记为f),或者更进一步说,他们是加法同构的(一定要注意,这个时候,对于一个负整数-y,它被这个同构对应到一个-xx..x,后面的xx…x是y的二进制表示)。当我们采用计算机的存储模式,即有限位位模式的二进制存储(首位作为符号位),不妨说是n位比特串。记m=2n,Bm为所有n位2进制元素组成的集合。那么显然B到Bm之间有一个同态,并且它是一个满同态,我们记为g. 于是我们显然得到一个交换图,也就是Z到Bm的同态g∘f(图3)。



这个同态就是补码的编码方式。事实上,f是直接的,正数就是二进制表示,负数就是绝对值的二进制表示前面加个负号。然后,对于g,我们必须把零元映到零元;为了让第一位表示符号位,我们充分利用剩余的位,即第一位为0的元素就是它的二进制表示。因此只需要再确定逆元即可,从而我们将-xx…x的负数,对应到它的绝对值xx…x所对应的Bm中元素的逆元。举例来说,4位模式,n=4,m=24=16. 则0111是对应最大的正数0111,那么-0111就对应于它在B16中的逆元1001,也就是4位模式中那个和0111相加等于10000的元素。要注意,在B16中10000就是0000,即零元。其他以此类推。最后,我们考虑多出来的1000,它要么对应1000要么对应-1000,这两者其实都是可以,但是为了与计算机的要求一致,它应该表示一个负数,所以对应-1000.

我们说明两点事情。第一,Bm就是计算机当n位存储模式下的计算方式,它是封闭的,并且它是一个加法群。第二,我们的目标是考虑Z的一个子集它的二进制表达在计算机中的合理编码及运算。通过上面这个复合同态g∘f,我们就已经得到了{−7,−6,…,6,7}的二进制表达,并且在不溢出的情况下,它是完美的计算体系。事实上,我们会发现问题麻烦的地方只在一正一负相加的情形,而我们的对应只需要将绝对值较大的那个数分成较小的那个数的逆加上另外一个数,然后由于一个数加它的逆刚好为零,所以抵消了那一部分,也就得出正确的结果。比如5-3=0101+(-0011)=0101+1101=(0010+0011)+1101=0010+(0011+1101)=0010+0000=0010.

最后,我们从上面的复合同态的定义方式可以知道补码的人工计算方式。我们需要强调,我们应该这样来看问题,即补码不是从原码来的,补码是一种映射或者说编码方式,它是一种有自己理论依据的完善的计算机处理整数的编码方式,是一个独立的体系。而原码是更接近人类理解的计算机的另一种整数存储方式。所以我们所谓的从原码求补码,是试图找这两个编码之间的关系(图4)。



我们最后说明为什么从原码求补码的方式是这样的,或者说我们解释为什么补码是如我们一开始定义的那样的。这其实很简单,我们只需要分析这个复合同态,结合B到原码的一一映射就可以知道。事实上,对于正数(我们指原码),我们看到,它的原码和补码是一样的;而对于负数1xx…x(除了100…0以外,这个数是补码仅有的,原码不会有这个数),我们看到,我们先求它的绝对值,即将符号位变为0,得到0xx…x. 然后再求这个绝对值映射到Bm之后(也就是样子完全没变)在Bm之中的逆元。而逆元就是和它相加等于00…0的位模式。要和0xx…x相加等于00…0,那显然就是考虑0xx…x从右数起的第一个1不动,其余左边的全部取反;或者说,这个逆元就是1xx…x最右边的1和符号位的1不动,两个1之间的数全部取反得到的。这就是这个原码的补码表示。

参考文献:

J.Brookshear,《计算机科学概论(第11版)》,人民邮电出版社。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息