前面講到布思算法的計算機底層模擬的時候,我們是借助於一個可以儲存、表示任意N位的二進制補碼的BinaryQueue實現的,現在我們模擬計算機底層整數除法還是要借助於它:
BinaryQueue類代碼:https://www.cnblogs.com/XT-xutao/p/10050518.html
我又寫了一個只基於二進制字符串的,更簡單,更方便
現在考慮計算機底層除法是怎樣實現的。
對於我們人工計算來說是比較簡單的,從高位一直到低位,一次次除,得出每一位的商,最后剩下余數即可。
計算機似乎也可以運用這種方式:
不過這只是無符號二進制整數,要是補碼形式的整數呢?
似乎是不可取的,因為如果被除數是負數,那么他前面的一系列 1 是不能作為判斷依據得出每一位的商的。
那么,怎么辦?
在計算機底層中,我們在ALU中利用幾個寄存器就可以搞定。
* M: 除數
* Q:被除數,被除數必須從N為算術擴展為2N位,但擴展為不存於Q中
* A:由於被除數需要擴展,我們用A來實現擴展位,
* 初始化: 若Q 大於等於0,初始化為0,即0...0
* 若Q小於0,則初始化為-1,即1...1
* Q0: Q最低位
除法定義如下:
被除數 = 除數 X 商 + 余數;(余數有正負)
於是我們有如下計算步驟:
* 1.A,Q左移一位
* 2.判斷:M 與 A
* 同號:A = A-M
* 異號:A = A+M
* 3.判斷:
* A未變號 或 A=0:Q0 = 1
* A變號 且 A!=0:Q0 = 0,A 恢復值
* 4.重復 1-3 步N次
* 5.余數在A中
* 判斷:被除數與除數
* 同號:商在Q中
* 異號:商為Q中取負數
於是可以有如下Java實現:
1
//直接用字符串形式,其他的一些方法是基於ALU,或者是其擴展,都易於實現的。
public String[] divide(String a, String b) { // a/b 2 String A = get01(a.length(), a.substring(0, 1));//附加寄存器,存放Q的擴展位,初始化為等位的被除數左擴位(1/0) 3 String Q = a; //被除數算術左擴為2n然后存到寄存器Q,注意A就是擴展位,擴展位不存在Q中,即AQM三個都等長 4 String M = b;//除數放到寄存器M 5 for (int i = 0; i < a.length(); i++) { 6 char A_sign = A.charAt(0); 7 8 //A Q 左移1位 9 String temp = shiftLeft(A + Q); 10 A = temp.substring(0, a.length()); 11 Q = temp.substring(a.length()); 12 13 //判斷MA是否同號 14 if (M.charAt(0) == A.charAt(0)) {// A M has same sign 15 A = substract(A, M).substring(1); 16 } 17 else { 18 A = add(A, M).substring(1); 19 } 20 21 //判斷A的符號變化沒有; 22 if (A_sign == A.charAt(0) || !A.contains("1")) {//A符號沒變,或者A=0(不含“1”即全0); 23 Q = Q.substring(0, Q.length() - 1) + "1"; //Q0=1 24 } 25 else {//A的符號變了,且A!=0 26 Q = Q.substring(0, Q.length() - 1) + "0"; //Q0=0 27 A = add(A, M).substring(1); //A =A+M 恢復; 28 } 29 } 30 String[] res = new String[2]; //返回0:商,1:余數 31 res[0] = (a.charAt(0) == b.charAt(0)) ? Q : getNegative(Q); 32 res[1] = A; 33 return res; 34 }
1 //用BinaryQueue
public class Division { 2 private BinaryQueue A,M ,Q; 3 private int len ; 4 private int n1,n2; 5 public Division(String dividend, String divisor){ // 假設是同樣長度的,如果不同長度再擴展
6 len = dividend.length();//位長度
7 boolean isSameSign = dividend.charAt(0)==divisor.charAt(0); 8 M = new BinaryQueue(divisor);//除數
9 Q = new BinaryQueue(dividend);//被除數
10
11 n1 = Q.getInt();//被除數
12 n2 = M.getInt();//除數 13
14 //初始化A為Q的擴展位
15 if (dividend.charAt(0)=='1') {// Q<=0
16 String ss=""; 17 for (int i =0;i<len;i++ )ss+="1"; 18 A = new BinaryQueue(ss); 19 } 20 else A = new BinaryQueue(len); 21
22
23 System.out.println(A.getStr()+ " "+ Q.getStr()+" "+M.getStr()+" 初始化"); 24
25 for (int i =0;i<len;i++){ 26 int a = A.getFirst();//提取A的最高位,一邊步驟3判斷A是否變號
27 boolean isAddation = false;// 記錄步驟2中是加操作還是減,以便步驟三恢復A的數值
28 //步驟1
29 A.shiftLeft(); 30 A.set(len-1,Q.get(0)); 31 Q.shiftLeft(); 32 //步驟2
33 if (A.getFirst()==M.getFirst()){ 34 A = A.subtract(M); 35 } 36 else { 37 A = A.add(M); 38 isAddation = true; 39 } 40 // 步驟3
41 if (A.isZero()||a==A.getFirst()) { 42 Q.set(len-1,1); 43 } 44 else { 45 Q.set(len-1,0); 46 if (isAddation) A = A.subtract(M); 47 else A = A.add(M); 48 } 49 System.out.println(A.getStr()+ " "+ Q.getStr()+" "+M.getStr()+" 第"+i+"周期"); 50 } 51
52 System.out.println(n1+"/"+n2 +" = "+ (isSameSign?Q.getInt():Q.getOppositeNumber().getInt())+"···"+A.getInt()); 53
54 } 55
58 public static void main(String[] args) { 59 new Division("10000","00011");
new Division("0110111010","0001000101");
new Division("1000001010","1111100101");
}
}
Demo:
A Q M
11111 10000 00011 初始化
11111 00000 00011 第0周期
11110 00000 00011 第1周期
11111 00001 00011 第2周期
11110 00010 00011 第3周期
11111 00101 00011 第4周期
-16/3 = -5···-1
0000000000 0110111010 0001000101 初始化
0000000000 1101110100 0001000101 第0周期
0000000001 1011101000 0001000101 第1周期
0000000011 0111010000 0001000101 第2周期
0000000110 1110100000 0001000101 第3周期
0000001101 1101000000 0001000101 第4周期
0000011011 1010000000 0001000101 第5周期
0000110111 0100000000 0001000101 第6周期
0000101001 1000000001 0001000101 第7周期
0000001110 0000000011 0001000101 第8周期
0000011100 0000000110 0001000101 第9周期
442/69 = 6···28
1111111111 1000001010 1111100101 初始化
1111111111 0000010100 1111100101 第0周期
1111111110 0000101000 1111100101 第1周期
1111111100 0001010000 1111100101 第2周期
1111111000 0010100000 1111100101 第3周期
1111110000 0101000000 1111100101 第4周期
1111111011 1010000001 1111100101 第5周期
1111110111 0100000010 1111100101 第6周期
1111101110 1000000100 1111100101 第7周期
1111111000 0000001001 1111100101 第8周期
1111110000 0000010010 1111100101 第9周期
-502/-27 = 18···-16