计算机中的数(二):定点数的移位运算和加减运算
参考资料:
张代远《计算机组成原理教程 第二版》清华大学出版社
唐朔飞《计算机组成原理》高等教育出版社
《2012计算机组成原理 联考复习指导》机械工业出版社
有两种移位方式,逻辑移位和算数移位。下面以一个8位二进制数1000 1011为例来探讨。
逻辑移位
把1000 1011视为一串二进制串,不对该串做任何解读,不管是原码还是补码还是别的什么,只管移位,移丢了就移丢了,多出来的空位一律补0。1000 1011逻辑左移一位成为0001 0110,逻辑右移一位成为0100 0101。
算数移位
算数移位本质上是为了实现有符号数的快速乘除法。算数移位想要达到的效果是:使得算数左移一位得到的结果是原数乘以2(在最高位没有溢出的情况下),使得算数右移一位得到的结果是原数除以2(忽略移丢的最低位)。原码、反码、补码的编码规则各不相同,在不同的编码规则下要达到同样的最终效果,那就必须对1000 1011这一串数字作出解读,根据这串数字的编码规则而采用不同的移位策略。为了达到上述目的,显然应该规定“无论左移还是右移,符号位是不应该因移位改变的”。那么剩下的工作就是看看移位之后空出来的位置应该补0还是应该补1。先看最简单的原码表示法。若1000 1011B是一个原码,则它表示的是-11D,算数左移时,符号位为1不变,其余位000 1011依次左移,最高位的0移丢了(移丢的是0,所以没有溢出,若此处移丢的是1,则溢出,结果出错),最低位空出来一位,若最低位补0,则最终结果为1001 0110B,表示-22D,就是将-11乘以2的结果。(此处的道理可参考王道论坛《2012 计算机组成原理 联考复习指导》2.2节,再看看唐朔飞《计算机组成原理》第二版P235表6.5和6.6,相信一切就很清楚了。)同样的道理,以此类推,可总结如下:
有符号数为正数时,原码、反码、补码的空位皆补0,因为它们对于正数的编码是相同的。有符号数为负数时
要注意区分正负数,比如整数补码的右移空位补0,而负数右移空位补1。.原码左移、原码右移得到的空位均补0反码左移、反码右移得到的空位均补1
补码左移得到的空位补1,补码右移得到的空位补0
汇编语言中常用移位指令有
SAL 算术左移指令(Shift Arithmetic Left)SAR 算术右移指令(Shift Arithmetic Right)SHL 逻辑左移指令(Shift Logic Left)SHR 逻辑右移指令(Shift Logic Right)
加减法运算这些指令都是针对补码的,因为计算机中的数实际上是用补码表示。例如有 AH=FFH(即-1),那么执行指令:SAR AH,1 后,AH仍为FFH。
原码的加减法
这个很好做,先把符号位摘出来,符号位不参加运算,然后按照平常的算数规则,判断数的正负等等等等....
补码的加减法
由于补码的种种好处,计算机中普遍采用补码进行运算。补码加减法的两大基本公式
[A+B]补 = [A]补 + [B]补
[A-B]补 = [A]补 + [-B]补 (对于这个公式的理解参考《补码初探》)
计算A+B时
不用判断A和B的正负,直接将[A]补、[B]补 送入加法逻辑中,得到结果。过程如图
图1.0加法逻辑并不区分哪是符号位哪是数值位,只是把送入的两个数当成两个无差别的数字串,从低位到高位按位相加(当然会处理好进位),由于寄存器长度的限制,最终如果最左一位(也即符号位)有进位,该进位会被舍掉(相当于对结果做一次模运算)。可见补码加法的逻辑十分简单,这有利于计算机的硬件设计。相加结果当然也是用补码表示的。如果判断没有溢出,说明这个结果正确,于是此次加法运算结束,若有溢出,则会产生中断,进行溢出处理。
计算A-B时
先求出-B的补码,再把[A]补、[-B]补 送入加法逻辑中,得到结果可见补码的减法运算被转换成了加法运算
已知B的补码求-B的补码的过程,称为“变补”。其运算规则为:将B的补码连同符号位各位取反、末位加一目前为止我们已经遇到了三种“各位取反、末位加一”的运算。
由真值求补码:原码除符号位外,每位取反,末位加1由补码求真值:将符号位写为负号“-”,其余各位按位取反,末位加1由B的补码求-B的补码:将B的补码连同符号位各位取反、末位加一为了方便记忆,总结其特点:这三种运算都可以先摘出符号位不管,将其余位逐位取反、末位加1,然后再来看符号位需要做什么改变。
由真值求补码,当然要保持补码和原码的符号位相同由补码求真值,真值的绝对值大小当然和符号位没什么关系由B的补码求-B的补码,其符号位当然要取反
溢出判断
说明:
原码、反码、补码运算的溢出判断是不同的,但计算机一半采用补码实现运算,所以以下主要研究补码加法的溢出判断。
真值溢出
若一个数x超出了n位二进制数的定义域范围,即x∉[-(2^n), (2^n)-1],则称真值x溢出。当运算过程中的某个结果x超出了机器所能表示的数的范围,则由于硬件的限制,x的某些位必须被舍掉,寄存器中留下的数据只是x的残渣,是不能正确反映计算结果的。所以计算机要能够判断每一次运算是否发生溢出,以便在溢出的时候进行处理(通常是停止计算并发出中断信号)。
符号扩展
当字长为4时,[+101]补=0101,对应的模M=10000,当字长为5时[+101]补=00101,对应的M=100000,依此类推,字长越长,[+101]补的前导0就越多,但是最后三位始终不变。从形式上来看,当字长增加时,相当于把原来最高位的0复制到新增加的位上,所以这一操作也称为“符号扩展”。这个原则对于“n位补码所能表示的最小负数”也是适用的,例如:[-8]补 = 1000 = 11000
变形补码和扩展位
判断补码溢出的一般方法一个n位二进制数的补码的位数必大于n,补码中比之n多出来的位称为扩展位。通常的补码只有一位扩展位,即符号位。但采用“双符号位”的话,就是两位扩展位。扩展位可以无限多,但根据“符号扩展”的原理,一个数A的补码的所有扩展位应相等,即全为1或全为0。“符号扩展”的本质是取一个更大的模,例如一个3位2进制数-111,若以2^4为模,则其补码为1001,若以2^5为模,其补码为11001,模取的越大,补码所能表示的数的范围也就越大,而溢出的本质是两数相加的和已超过了n位字长(由于硬件所限)所能表示的范围。所以为了防止溢出,我们可以取更大的模,即在硬件上设计更大的字长。
定理Main:一个任意位的二进制数X,将X存储在n位寄存器时,发生真值溢出(即X∉[-(2^n), (2^n)-1])的充要条件是:存在正整数K,使得X的以2^(n+K)为模的补码的高K位不全为1或不全为0。证明见清华大学张代远《计算机组成原理教程》第二版 定理2.9处理多个数相加的情况:例如有三个数x、y、z,其中x、y为正,z为负,求(x+y)+z
一种方法是在中间计算时提供足够多的扩展位,比如双符号位,以保证中间计算的结果不可能溢出。我们只对最终结果的扩展位进行检测,看其是否溢出,只要(x+y)+z的最终结果不溢出,则算出来的这个值就是正确的值。这种方法的缺点是需要在硬件上提供充足的扩展位。
还有一种办法是不提供过多的扩展位,在中间计算的每一步都检测是否溢出,任何一步一旦发生溢出,立刻终止运算,向用户提交溢出报告。
以下给出三种常用的判断单步加法溢出的方法:采用1位符号位时
若两操作数的符号位不同,则不可能发生溢出。当两操作数符号位相同时,有两种判断溢出的方法
1)若操作数符号位和运算结果符号位相同,则未溢出;若操作数符号位和运算结果符号位不相同,则溢出。简称“不同溢。”2)通过比较最高数值位的进位和符号位的进位来判断是否溢出。这二者相同说明未溢出,不同说明溢出,简称“不同溢。”这两种方法都是“不同溢”
采用双符号位时
所谓的“双符号位”,实际上是把模扩大一倍之后的补码。本质上来讲,仍然只有最左边一位是“符号位”,剩下的都是“数值位”,只不过对于所有的真值X(X∉[-(2^n),(2^n)-1]),其前两位或为11,或位00,即最高数值位和符号位总是相同,所以看起来仿佛最高两位都是“符号位”。由定理Main可知,当运算结果的高两位为00或11时,表明结果未溢出(即真实结果仍在[-(2^n),(2^n)-1]范围内),否则为溢出。简称“不同意”。
举个例子:用双符号位补码计算7+3
[7]补 =00|110 , [3]补 = 00|011, 00|110 + 00|011 = 01|001,前两位是01,发生了溢出。事实上3+6=9,若得到的结果01001视为一位符号的补码,则01001正好就是9,既然结果正确,那为什么说它“溢出”了呢? 事实上,如果了解到“加法器的字长和通用寄存器的字长未必相等”,那这个问题就很容易理解了。现假设通用寄存器为4位,加法器为5位,在通用寄存器中存储了两个数,在将这两个数送到加法器中时,自动把通用寄存器中的一位符号值同时送到加法器中的两个符号位中去(实现了位扩展),在加法器中计算完毕后,检查两个符号位的值是否相等,若相等,说明结果能够用4位通用寄存器存放,则将结果送出,送出时砍掉一个符号位,若不相等,说明结果无法用4位通用寄存器存放,于是中断计算,向用户发出溢出报告,虽然发出了溢出报告,但在加法器内部运算时,却是实实在在地得到了正确的计算结果,只不过这个结果无法正确送出。
当运算结果的高位为10时,表明实际结果是一个负数,是两个负数相加超过了可表示的最大范围,所以是下(负)溢,当运算结果的高位为01时,是上(正)溢
posted on 2011-10-26 12:22 ybwang1989 阅读(7888) 评论(0) 编辑 收藏 举报