计算机基础必知必会——原码、反码与补码
⽬录
引⾔
我们知道计算机所能处理的数都是⼆进制的。准确的说,我们的硬件设备只能对由纯01序列组成的、⽆符号且固定位长的⼆进制整数进⾏运算。
为了⽅便起见,如⽆特殊说明,以下称⼆进制数,均专指 由纯01序列组成的、⽆符号且固定位长的⼆进制整数
但是现实⽣活中我们要处理的数字有正数、有负数,有整数、有⼩数。为了让计算机能够处理⼩数的运算,⼈们提出了浮点数的概念,并设计了相关硬件单元(这已经超出了本⽂的讨论范围,就不在展开了);⽽为了能让我们的计算机对正负数进⾏(算数)运算,⼈们发明了原码、反码和补码。
需要解决的问题
前⾯提到,我们的计算机硬件设备(CPU)只能对⼆进制数进⾏运算。现在为了让我们的计算机能够处理正负数,我们需要解决的问题有两个:如何使⽤⼆进制数(或者说⽤⼆进制的01序列)表⽰正数和负数。
如何⽤⼆进制数的运算来表⽰正负数的(算数)运算。也就是说,我们需要⼀种将正负数表⽰为⼆进制数的⽅案,这⾥我们将这类⽅案成为数的⼆进制编码,简称编码。如果将某⼀个数X 的编码记做,那么我们的编码⽅案应该满⾜能够之间的运算或这些运算构成的算法来表⽰X之间的运算。
即,对于整数X、Y之间的算数运算¤(¤为加减乘除等),如果存在由⼆进制数之间的运算构成的算法alg,使得
那么我们就可以说在这种编码下,可以⽤⼆进制数的运算来表⽰正负数的(算数)运算。
运算与硬件
在开始介绍原码、反码与补码之前我们需要先讨论⼀些关于运算与计算机硬件的关系的知识。
1. CPU有专门对⼆进制数进⾏运算的硬件单元,称为算术逻辑单元ALU。ALU除了能对⼆进制数进⾏的运算,除了加减乘除等算数运算之外,还有与、或、⾮、异或等位逻辑运算和移位运算。
2. 在ALU中,加减乘除这些算数运算有特定的电路完成,如加法器、乘法器、除法器。没有减法器的原因是,利⽤补码可以将减法与加法统⼀,关于这⼀点,后⾯会详细讨论。
3. 虽然算数运算远不⽌加减乘除这四种,但是ALU仅提供了对这四种运算的⽀持,其他的算数运算由软件算法基于这四种运算来完成。
4. 乘法器和除法器都是由加法器构成的。事实上⼆进制数的乘法和除法运算都可以拆解为加法和减法运算(对这⽅⾯内容感兴趣的,可百度乘法器和除法器)。也就是说只要我们的编码⽅案能够完成加减运算,那么就⼀定可以满⾜加减乘除四则运算。
原码
为了表⽰正负数,⼈们规定⼆进制数的最⾼位为符号位,符号位为0则表⽰该数为正数,符号位为1表⽰该数为负数。
⼀种最简单的⽅案就是最⾼位为符号位,剩下的为数值位,数值位数值是多少,则所代表数的绝对值就是多少。⼈们将这个⽅案成为原码。例如,在4位字长的机器上:
5的原码:,-5的原码:原码的问题
原码是⼀种最简单、最直接的表⽰⽅案,但是直接使⽤原码会给带来⼀些问题。
(X )编(X )编alg ((X ),(Y ))=编编(X ¤Y )编
(5)=原0101(−5)=原1101
1. 零的表⽰不唯⼀例如在⼋位字长的机器上,整数零会有两种表⽰⽅法:1000 0000 和 0000 0000。
2. 原码加减法运算繁杂例如 -3 + 5 =2,在4位字长的机器上:-3被表⽰为 1011,5被表⽰为0101,⽽1011 + 0101 = 10000,结果显然不正确。并且由于溢出的存在,⾼位被截断,所以最终结果为0000,即0。
只要有⼀个操作数为负数,我们就不能直接⽤原码的加减来表⽰两数的加减。
即,当x、y有⼀个符号为负时,但是有⼀种情况例外,当且仅当x、y均为正时,事实上,为了能通过原码来处理负数的加减运算,需要判断参与运算的两个数的正负,然后根据正负进⾏不同的处理,并且需要将符号位与数值位分开处理,这会导致使⽤原码来表⽰正负数加减法运算会⼗分繁杂。
由于以上两个原因,⼈们发明了反码与补码。在下⾯的内容你可以看到,补码是如何⽤唯⼀的编码表⽰零,以及如何做到直接⽤补码的加法运算来表⽰正负数的加减运算。
反码
反码是当⼀个数为正数时(即符号位为0),其反码与原码相同;这个数为负数时(即符号位为1),其反码为原码的数值位按位取反,符号位不变。
可以看出,⼀个负数所对应正数的原码,跟这个负数的反码相加,得到的结果的所有位都是1, 即对于⼀个n位的负数 -x(x>0),有:例如 -5:
,,反码是求补码的过渡码,只需要记住怎么求即可。
溢出与借位
溢出
溢出是⼀个⾮常重要的概念,溢出是指计算机⽆法存放过⼤或者过⼩的数字,从⽽产⽣截断,导致存放数数字与实际的数字不符。
例如 java的byte类型,长度为⼋位,最⾼位为符号位,最⼤可存放的数值范围为 -128~+127,如果存⼊了+128,那么就产⽣了溢出,⾼位会被截断,只保留低位的值。例如前⾯将 ⼆进制的10000存⼊四位字长,变成了0000。
在字长为n的机器上,⼀个字所能存储的最⼤的值为 ⼆进制的11111…共n个1,转成⼗进制就是,如果再加⼀,由于溢出会变成0。
补码就是利⽤了溢出后⾼位被截断的特点。
借位
与溢出相对的就是借位,当⼀个⼩数减⼤数时就会产⽣借位。
例如,在四位机器上:补码
⼀个正数的补码,就是它的原码;⽽⼀个负数的补码,就是它的反码加⼀。
例如,在4位机器上:时,根据,可得:
,即:.
x ±原y =原 (x ±y )原
x ±原y =原(x ±y )原
x +原(−x )=反=2−n 1
5=原0101−5=反10105+原(−5)=反1111=2−41
2−n 10010−0100=1110两个负数的补码相加
3=原0011,3=反0011,3=补0011
−3=原1011,−3=反1100,−3=补1101
x >0(−x )=反2−n 1−x 原(−x )=补(−x )+反1=2−n x 原(−x )=补2−n x 原
我们说引⼊补码,主要是为了克服原码所存在的问题。
使⽤补码我们可以将零的表⽰唯⼀化:
下⾯我们就讨论补码是如何做到直接⽤补码的加法运算来表⽰正负数的加减运算。
补码与正负数加法运算我们可以将任意两个正数或负数的加减运算转化为如下三种形式之⼀:正数 + 正数正数 + 负数
负数 + 负数
⼤家可以证明⼀下这句话的正确性,这⾥就不展开了。
补码处理 正数+正数
对于 正数+正数 的情况,因为正数的补码与原码相同,⼜因为前⾯已经证明过当x、y均为正时: ,所以:所以两正数的补码之和,为两正数之和的补码。
补码处理 正数+负数
对于 正数+负数 的情况,假设 x、y 均为正数,x + (-y) = x - y = - (y - x).
前⾯已经证明过:即:所以正数和负数的补码之和,为正数和负数之和的补码。
补码处理 负数 + 负数
对于 负数 + 负数 的情况,假设 x、y 均为正数,(-x) + (-y) = -(x + y) .
即:前⾯提到过,对于,由于溢出,后⾯的就可以省略,因为可以证明。所以 和 最终存储在计算机中的值是⼀样的。
故,即,两负数的补码之和,为两负数之和的补码。
结论
基于以上证明,我们可以得出结论,任意两整数的加法运算,我们都可以使⽤这两个整数的补码相加来表⽰,即,对于任意两整数(正数、负数或零),有:使⽤补码的好处
该部分内容参考了百度百科,部分表述直接摘⾃百度百科。
(+0)=补(+0)=反(+0)=原00000000(−0)=补11111111+1=00000000
x ±原y =原(x ±y )原x +补y =补(x +y )补
(−x )=补2−n x 原
x +补(−y )=补x +补2−n y =原x +原2−n y =原2−n (y −原x )
原(x +(−y ))=补(−(y −x ))=补2−n (y −x )=原2−n (y −原x )
原x +补(−y )=补(x +(−y ))补
(−x )+补(−y )=补2−n x +原2−n y =原2−n (x +原y )+原2n
((−x )+(−y ))=补(−(y +x ))=反2−n (y +x )=原2−n (y −原x )
原(−x )+补(−y )=补((−x )+(−y ))补
2−n (x +原y )+原2n 2n 2n 2−n (x +原y )≥原02−n (x +原y )+原2n 2−n (x +原y )原(−x )+补(−y )=补((−x )+(−y ))补
x 、y x +补y =补(x +y )补
1. 使⽤补码,可以将加法和减法统⼀为加法,cpu就不载需要提供减法器,使⽤加法器就可以完成加法与减法运算,简化了硬件结构。
2. 克服了原码加减法运算繁杂的弊端,因为原码的加减运算需要判断符号、需要考虑数值位的溢出等。使⽤补码,可以直接⽤补码的加
法运算表⽰正负数加减法。
3. 使零的表⽰变得唯⼀。
4. 在计算机中,利⽤电⼦器件的特点实现补码和真值、原码之间的相互转换,⾮常容易。
5. 补码表⽰统⼀了符号位和数值位,使得符号位可以和数值位⼀起直接参与运算,这也为后⾯设计乘法器除法器等运算器件提供了极⼤
的⽅便。