算数移位本质上是为了实现有符号数的快速乘除法。算数移位想要达到的效果是:使得算数左移一位得到的结果是原数乘以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,相信一切就很清楚了。)
同样的道理,以此类推,可总结如下:
一个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
1)
若操作数符号位和运算结果符号位相同,则未溢出;若操作数符号位和运算结果符号位不相同,则溢出。简称“不同溢。”
2)通过比较最高数值位的进位和符号位的进位来判断是否溢出。这二者相同说明未溢出,不同说明溢出,简称“不同溢。”
这两种方法都是“不同溢”
所谓的“双符号位”,实际上是把模扩大一倍之后的补码。本质上来讲,仍然只有最左边一位是“符号位”,剩下的都是“数值位”
,只不过对于所有的真值X(X∉[-(2^n),(2^n)-1]),其前两位或为11,或位00,即最高数值位和符号位总是相同,所以看起来仿佛最高两位都是“符号位”。
由定理Main可知,当运算结果的高两位为00或11时,表明结果未溢出(即真实结果仍在[-(2^n),(2^n)-1]范围内),否则为溢出。简称“不同意”。
[7]补 =00|110 , [3]补 = 00|011, 00|110 + 00|011 = 01|001,前两位是01,发生了溢出。事实上3+6=9,若得到的结果01001视为一位符号的补码,则01001正好就是9,既然结果正确,那为什么说它“溢出”了呢? 事实上,如果了解到“加法器的字长和通用寄存器的字长未必相等”,那这个问题就很容易理解了。现假设通用寄存器为4位,加法器为5位,在通用寄存器中存储了两个数,在将这两个数送到加法器中时,自动把通用寄存器中的一位符号值同时送到加法器中的两个符号位中去(实现了位扩展),在加法器中计算完毕后,检查两个符号位的值是否相等,若相等,说明结果能够用4位通用寄存器存放,则将结果送出,送出时砍掉一个符号位,若不相等,说明结果无法用4位通用寄存器存放,于是中断计算,向用户发出溢出报告,虽然发出了溢出报告,但
在加法器内部运算时,却是实实在在地得到了正确的计算结果,只不过这个结果无法正确送出。