计算机两负数相加,怎么让加法器实现两个负数相加
请点评
我们已经了解了计算机中正整数如何表⽰,加法如何计算,那么负数如何表⽰,减法⼜如何计算呢?本节讨论这些问题.为了书写⽅便,本节举的例⼦都⽤8个bit表⽰⼀个数,实际计算机做整数加减运算的操作数可以是8位、16位、32位甚⾄64位的.
要⽤8个bit表⽰正数和负数,⼀种简单的想法是把最⾼位规定为符号位(Sign Bit),0表⽰正1表⽰负,剩下的7位表⽰绝对值的⼤⼩,这称为Sign and Magnitude表⽰法.例如-1表⽰成10000001,+1表⽰成00000001.这样⽤8个bit表⽰整数的取值范围是-27-1~27-1,即-127~127.
采⽤这种表⽰法,计算机做加法运算需要处理以下逻辑:
如果两数符号位相同,就把它们的低7位相加,符号位不变.如果低7位相加时在最⾼位产⽣进位,说明结果的绝对值⼤于127,超出7位所能表⽰的数值范围,这称为溢出(Overflow)[24],这时通常把计算机中的⼀个标志位置1表⽰当前运算产⽣了溢出.
如果两数符号位不同,⾸先⽐较它们的低7位谁⼤,然后⽤⼤数减⼩数,结果的符号位和⼤数相同.
那么减法如何计算呢?由于我们规定了负数的表⽰,可以把减法转换成加法来计算,要计算a-b,可以先把b变号然后和a相加,相当于计算a+(-b).但如果两个加数的符号位不同就要⽤⼤数的绝对值减⼩数的绝对值,这⼀步减法计算仍然是免不了的.我们知道加法要进位,减法要借位,计算过程是不同的,所以除了要有第 1 节 "为什么计算机⽤⼆进制计数"提到的加法器电路之外,还要另外有⼀套减法器电路.
如果采⽤Sign and Magnitude表⽰法,计算机做加减运算需要处理很多逻辑:⽐较符号位,⽐较绝对值,加法改减法,减法改加法,⼩数减⼤数改成⼤数减⼩数……这是⾮常低效率的.还有⼀个缺点是0的表⽰不唯⼀,既可以表⽰成10000000也可以表⽰成00000000,这进⼀步增加了逻辑的复杂性,所以我们迫切需要重新设计整数的表⽰⽅法使计算过程更简单.
本节介绍⼀种⼆进制补码表⽰法,为了便于理解,我们先看⼀个⼗进制的例⼦:
167-52=167+(-52)=167+(999-52)-1000+1=167+947-1000+1=1114-1000+1=114+1=115
167-52 → 减法转换成加法 167+(-52) → 负数取9的补码表⽰ 167+947 → 114进1 → ⾼位进的1加到低位上去,结果为115
在这个例⼦中我们⽤三位⼗进制数字表⽰正数和负数,具体规定如下:
表14.5. 9's Complement表⽰法
数值 补码表⽰
⾸先-52要⽤999-52表⽰,就是947,这称为取9的补码(9's Complement);然后把167和947相加,得到114进1;再把⾼位进的1加到低位上去,得115,本来应该加1000,结果加了1,少加了999,正好把先前取9的补码多加的999抵消掉了.我们本来要做167-52的减法运算,结果变成做999-52的减法运算,后者显然要容易⼀些,因为没有借位.这种补码表⽰法的计算规则⽤⼀句话概括就是:负数⽤9的补码表⽰,减法转换成加法,计算结果的最⾼位如果有进位则要加回到最低位上去.要验证这条规则得考虑四种情况:
两个正数,相加得正
⼀正⼀负,相加得正
⼀正⼀负,相加得负
两个负数,相加得负
我们举的例⼦验证了第⼆种情况,另外三种情况请读者⾃⼰验证,暂时不考虑溢出的问题,稍后会讲到如何判定溢出.
上述规则也适⽤于⼆进制: 负数⽤1的补码(1's Complement)表⽰,减法转换成加法,计算结果的最⾼位如果有进位则要加回到最低位上去 .取1的补码更简单,连减法都不⽤做,因为1-1=0,1-0=1,取1的补码就是把每个bit取反,所以1的补码也称为反码.⽐如:
00001000-00000100 → 00001000+(-00000100) → 00001000+11111011 → 00000011进1 → ⾼位进的1加到低位上去,结果为00000100两个负数的补码相加
1's Complement表⽰法相对于Sign and Magnitude表⽰法的优势是⾮常明显的:不需要把符号和绝对值分开考虑,正数和负数的加法都⼀样算,计算逻辑更简单,甚⾄连减法器电路都省了,只要有⼀套加法器电路,再有⼀套把每个bit取反的电路,就可以做加法和减法运算.如果8个bit 采⽤1's Complement表⽰法,负数的取值范围是从10000000到11111111(-127~0),正数是从00000000到01111111(0~127),仍然可以根据最⾼位判断⼀个数是正是负.美中不⾜的是0的表⽰仍然不唯⼀,既可以表⽰成11111111也可以表⽰成00000000,为了解决这最后⼀个问题,我们引⼊2's Complement表⽰法.
2's Complement表⽰法规定:正数不变,负数先取反码再加1.如果8个bit采⽤2's Complement表⽰法,负数的取值范围是从10000000到11111111(-128~-1),正数是从00000000到01111111(0~127),也可以根据最⾼位判断⼀个数是正是负,并且0的表⽰是唯⼀的,⽬前绝⼤多数计算机都采⽤这种表⽰法.为什么称为" 2的补码 "呢?因为对⼀位⼆进制数b取补码就是1-b+1=10-b,相当于从2⾥⾯减去b.类似地,要表⽰-4
需要对00000100取补码,11111111-00000100+1=100000000-00000100,相当于从28⾥⾯减去4.2's Complement表⽰法的计算规则有些不同:减法转换成加法,忽略计算结果最⾼位的进位,不必加回到最低位上去.请读者⾃⼰验证上⼀节提到的四种情况下这条规则都能算出正确结果.
8个bit采⽤2's Complement表⽰法的取值范围是-128~127,如果计算结果超出这个范围就会产⽣溢出,例如:
图14.3. 有符号数加法溢出
如何判断产⽣了溢出呢?我们还是分四种情况讨论:如果两个正数相加溢出,结果⼀定是负数;如果两个负数相加溢出,结果⼀定是正数;⼀正⼀负相加,⽆论结果是正是负都不可能溢出.
图14.4. 如何判定溢出
从上图可以得出结论: 在相加过程中最⾼位产⽣的进位和次⾼位产⽣的进位如果相同则没有溢出,如果不同则表⽰有溢出 .逻辑电路的实现可以把这两个进位连接到⼀个异或门,把异或门的输出连接到溢出标志位.
3.4. 有符号数和⽆符号数 请点评
前⾯⼏节我们⽤8个bit表⽰正数和负数,讲了三种表⽰法,每种表⽰法对应⼀种计算规则,这称为有符号数(Signed Number);如果8个bit全部表⽰正数则取值范围是0~255,这称为⽆符号数(Unsigned Number).其实计算机做加法时并不区分操作数是有符号数还是⽆符号数,计算过程都⼀样,⽐如上⾯的例⼦也可以看作⽆符号数的加法:
图14.5. ⽆符号数加法进位
如果把这两个操作数看作有符号数-126和-8相加,计算结果是错的,因为产⽣了溢出;但如果看作⽆符号数130和248相加,计算结果是122进1,也就是122+256,这个结果是对的.计算机的加法器在做完计算之后,根据最⾼位产⽣的进位设置 进位标志 ,同时根据最⾼位和次⾼位产⽣的进位的异或设置 溢出标志 .⾄于这个加法到底是有符号数加法还是⽆符号数加法则取决于程序怎么理解了,如果程序把它理解成有符号数加法,下⼀步就要检查溢出标志,如果程序把它理解成⽆符号数加法,下⼀步就要检查进位标志.通常计算机在做算术运算之后还可能设置另外两个标志,如果计算结果的所有bit都是零则设置 零标志 ,如果计算结果的最⾼位是1则设置 负数标志 ,如果程序把计算结果理解成有符号数,也可以检查负数标志判断结果是正是负.
[24] 有时候会进⼀步细分,把正整数溢出称为上溢(Overflow),负整数溢出称为下溢(Underflow),详见strtol(3).