[深入理解計算機系統] 計算機如何實現乘法與除法運算
乘法運算
(1)原碼一位乘法
演算法要點:(1)乘法通過加法和移位來實現。兩個5位二進位制數(最高位為符號位)相乘,共需要進行4次加法和4次移位。
(2)部分積總是先加上被乘數或零,然後右移1位得到新的部分積。
(3)部分積是加上被乘數還是加上零,取決於乘數中當前考慮的數位。該數位為1,則加上被乘數然後右移,否則直接右移。
(4)乘數中的數位一旦被考慮過,將不再需要,可以通過右移丟棄。
(5)部分積加上被乘數的過程中,數值最高位產生的進位可能會佔據符號位。但是由於乘法中符號位是單獨處理,所以被乘數,乘數和部分積都被認為是無符號位數,而且做完加法之後肯定要右移1位,因此這裡出現的“進位佔據符號位”的現象不算“溢位”,右移後符號位0將還原。
例:已知X=0.1010B,Y=-0.0111B,用原碼一位乘法求X*Y=?
因為X=0.1010B,Y=-0.0111B,所以
[X]原=0.1010,X*=0.1010,x0=0;
[Y]原=1.0111,Y*=0.0111,y0=1;
部分積 乘數 註釋
0.0000 0111 此時乘數為0111,開始時,部分積Z0=0;
+0.1010 乘數中當前考慮的數位為1,加上X*
0.1010
0.0101 -> (0右移) 0 011 (1右移捨去) 右移一位得新部分積Z1
+0.1010 乘數中當前考慮的數位為1,加上X*
0.1111
0.0111 -> (1右移) 10 01 (1右移捨去) 右移一位得新部分積Z2
+0.1010 乘數中當前考慮的數位為1,加上X*
1.0001
0.1000 -> (1右移) 110 0 (1右移捨去) 右移一位得新部分積Z3
+0.0000 乘數中當前考慮的數位為0,加上0
0.1000
0.0100 0110 右移一位得新部分積Z4
[X*Y]原 的符號位=x0⊕y0=0⊕1=1
所以X* * Y* =0.01000110 ,則[X*Y]原=1.01000110,X*Y=-0.01000110
原碼一位乘法的實現雖然比較簡單,但是由於計算機都是採用補碼來進行加,減運算,如果為了做乘法運算而將運算元從補碼轉換成原碼,運算結束後再從原碼轉換成補碼,這樣就增加了許多操作步驟,延遲了計算時間。為此,人們又研究了補碼乘法方法,實現了基於補碼的乘法器,這樣就進一步提高了乘法運算的效率。
(2)補碼一位乘法
補碼一位乘法是把乘數Y補碼的符號位設成0,當做一個正數,與被乘數補碼相乘。乘法運算的步驟與原碼乘法相同。乘積出來後,如果Y是負數,則加上[-X]補得到[X*Y]補;否則乘積就直接等於[X*Y]補。這樣的補碼一位乘法也稱校正法。
例:已知X=0.1010B,Y=-0.0111B,用校正法求X*Y=?
被乘數補碼為[X]補=0.1010B,乘數補碼為[Y]補=1.1001。由於Y是負數,所以將其符號位去掉,故參與運算的乘數是0.1001。乘積出來後加上[-X]補進行校正。[-X]補=1.0110。
考慮到運算時可能會出現部分積的絕對值大於1的情況,所以被乘數和部分積採用雙符號位。
部分積 乘數 註釋
00.0000 1001 此時乘數為1001,開始時,部分積Z0=0;
+00.1010 乘數中當前考慮的數位為1,加上[X]補
00.1010
00.0101 -> (0右移) 0 100 (1右移捨去) 右移一位得新部分積Z1
+00.0000 乘數中當前考慮的數位為0,加上0
00.0101
00.0010 -> (1右移) 10 10 (0右移捨去) 右移一位得新部分積Z2
+00.0000 乘數中當前考慮的數位為0,加上0
00.0010
00.0001 -> (0右移) 010 1 (0右移捨去) 右移一位得新部分積Z3
+00.1010 乘數中當前考慮的數位為1,加上[X]補
00.1011
00.0101 -> (1右移) 1010 (1右移捨去) 右移一位得新部分積Z4
+11.0110 加上[-X]補進行校正
11.1011 1010
結果為[X*Y]補=1.10111010,所以X*Y=-0.01000110B。
採用補碼進行乘法,乘積的符號是在計算中自然得到的,這是補碼運算的一個共同特點,也是與原碼乘法的一個重要區別。
(3)布斯演算法
上述校正法的運算過程與乘數的符號有關。雖然可以將被乘數與乘數交換位置,使得乘數儘可能為正,以避免校正操作。但是當被乘數與乘數均為負數時,校正操作就不可避免了,所以校正法的控制邏輯比較複雜。為此,英國計算機專家布斯(A.D.Booth)於1956年提出了不用考慮運算元符號,可以用統一的規則進行計算的“布斯演算法”。
yi-1yi | yi-yi-1 | 操作 |
00 | 0 | 加上0,即直接右移一位 |
01 | 1 | 加上[X]補,再右移一位 |
10 | -1 | 加上[-X]補,再右移一位 |
11 | 0 | 加上0,即直接右移一位 |
例:已知X=0.1010B,Y=-0.0111B,用布斯演算法求X*Y=?
被乘數補碼為[X]補=0.1010B,乘數補碼為[Y]補=1.1001,校正數[-X]補=1.0110。
部分積 乘數 附加位 註釋
00.0000 1100 1 0 此時乘數為11001,開始時,部分積Z0=0;
+11.0110 yi-1 yi yi-1yi為10,加上[-X]補
11.0110
11.1011 -> (0右移) 0 110 0 1 右移一位得新部分積Z1
+00.1010 yi-1yi為01,加上[X]補
00.0101
00.0010 -> (1右移) 10 11 0 0 右移一位得新部分積Z2
+00.0000 yi-1yi為00,加上0
00.0010
00.0001 -> (0右移) 010 1 1 0 右移一位得新部分積Z3
+11.0110 yi-1yi為10,加上[-X]補
11.0111
11.1011 -> (1右移) 1010 1 1 右移一位得新部分積Z4
+00.0000 yi-1yi為11,加上0
11.1011 1010
1 第5步運算結束後,不再右移
除法運算
(1)原碼恢復餘數除法
原碼除法的特點是:符號位單獨處理,運算元的絕對值相除。
例:設被除數X=+0.1010B,除數Y=-0.1101B。求X/Y=?
[X]原=0.1010B,[Y]原=1.1101B,所以商的符號等於0⊕1=1。
記X的絕對值為X*=0.1010B,Y的絕對值為Y*=0.1101B。則商的絕對值等於X*/Y*。
由於要做“餘數(被除數)減去除數”的操作,而在計算機中這個操作是通過“餘數(被除數)加上負的除數的補碼”來完成的,所以給出[-Y*]補。
因為[Y*]補=0.1101B,所以[-Y*]補=1.0011B。
操作 被除數(當前餘數) 填商 註釋
00.1010
加上 [-Y*]補 +11.0011
11.1101 0 餘數為負,上商0
加上[Y*]補 + 00.1101
00.1010 恢復餘數
左移1位 01.0100 0
加上 [-Y*]補 +11.0011
00.0111 01 餘數為正,上商1
左移1位 00.1110 01
加上 [-Y*]補 +11.0011
00.0001 011 餘數為正,上商1
左移1位 00.0010 011
加上 [-Y*]補 +11.0011
11.0101 0110 餘數為負,上商0
加上[Y*]補 +00.1101
00.0010 恢復餘數
左移1位 00.0100 0110
加上 [-Y*]補 +11.0011
11.0111 01100 餘數為負,上商0
加上[Y*]補 +00.1101
00.0100 恢復餘數(最終餘數)
所以X*/Y*的商是0.1100B,餘數為0.0100B*2^(-4)=0.00000100B
由於在這個原碼演算法中,一旦上商0。就需要恢復餘數後才能計算下一位商。故稱其為“恢復餘數除法”。由於它在計算過程中需要恢復餘數,所以運算速度變慢,而且恢復餘數的次數事先是未知的,所以運算步數不能預先確定,這會使控制邏輯變得複雜。
(2)原碼加減交替除法
操作 被除數(當前餘數) 填商 註釋
00.1010
加上 [-Y*]補 +11.0011 減去除數
11.1101 0 餘數為負,上商0
左移1位 11.1010 0
加上[Y*]補 +00.1101 加上除數00.0111 01 餘數為正,上商1
左移1位 00.1110 01
加上 [-Y*]補 +11.0011 減去除數
00.0001 011 餘數為負,上商0
左移1位 00.0010 011
加上 [-Y*]補 +11.0011 減去除數
11.0101 0110 餘數為負,上商0
左移1位 10.1010 0110
加上[Y*]補 +00.1101 加上除數
11.0111 01100 餘數為負,上商0
加上[Y*]補 +00.1101 加上除數
00.0100 恢復餘數(最終餘數)
所以X*/Y*的商是0.1100B,餘數為0.0100B*2^(-4)=0.00000100B
對於計算機的定點小數除法而言,商也必須是定點小數。這就要求被除數的絕對值必須小於除數的絕對值、也就是說,第一次上商必須是0,否則視為“溢位”。
定點整數同理。