前面讲到布思算法的计算机底层模拟的时候,我们是借助于一个可以储存、表示任意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实现:
   //直接用字符串形式,其他的一些方法是基于ALU,或者是其扩展,都易于实现的。
public String[] divide(String a, String b) { // a/b
String A = get01(a.length(), a.substring(0, 1));//附加寄存器,存放Q的扩展位,初始化为等位的被除数左扩位(1/0)
String Q = a; //被除数算术左扩为2n然后存到寄存器Q,注意A就是扩展位,扩展位不存在Q中,即AQM三个都等长
String M = b;//除数放到寄存器M
for (int i = 0; i < a.length(); i++) {
char A_sign = A.charAt(0); //A Q 左移1位
String temp = shiftLeft(A + Q);
A = temp.substring(0, a.length());
Q = temp.substring(a.length()); //判断MA是否同号
if (M.charAt(0) == A.charAt(0)) {// A M has same sign
A = substract(A, M).substring(1);
}
else {
A = add(A, M).substring(1);
} //判断A的符号变化没有;
if (A_sign == A.charAt(0) || !A.contains("1")) {//A符号没变,或者A=0(不含“1”即全0);
Q = Q.substring(0, Q.length() - 1) + "1"; //Q0=1
}
else {//A的符号变了,且A!=0
Q = Q.substring(0, Q.length() - 1) + "0"; //Q0=0
A = add(A, M).substring(1); //A =A+M 恢复;
}
}
String[] res = new String[2]; //返回0:商,1:余数
res[0] = (a.charAt(0) == b.charAt(0)) ? Q : getNegative(Q);
res[1] = A;
return res;
}

 
 //用BinaryQueue

   public class Division {
private BinaryQueue A,M ,Q;
private int len ;
private int n1,n2;
public Division(String dividend, String divisor){ // 假设是同样长度的,如果不同长度再扩展
len = dividend.length();//位长度
boolean isSameSign = dividend.charAt(0)==divisor.charAt(0);
M = new BinaryQueue(divisor);//除数
Q = new BinaryQueue(dividend);//被除数 n1 = Q.getInt();//被除数
n2 = M.getInt();//除数 //初始化A为Q的扩展位
if (dividend.charAt(0)=='1') {// Q<=0
String ss="";
for (int i =0;i<len;i++ )ss+="1";
A = new BinaryQueue(ss);
}
else A = new BinaryQueue(len); System.out.println(A.getStr()+ " "+ Q.getStr()+" "+M.getStr()+" 初始化"); for (int i =0;i<len;i++){
int a = A.getFirst();//提取A的最高位,一边步骤3判断A是否变号
boolean isAddation = false;// 记录步骤2中是加操作还是减,以便步骤三恢复A的数值
        //步骤1
A.shiftLeft();
A.set(len-1,Q.get(0));
Q.shiftLeft();
        //步骤2
if (A.getFirst()==M.getFirst()){
A = A.subtract(M);
}
else {
A = A.add(M);
isAddation = true;
}
// 步骤3
if (A.isZero()||a==A.getFirst()) {
Q.set(len-1,1);
}
else {
Q.set(len-1,0);
if (isAddation) A = A.subtract(M);
else A = A.add(M);
}
System.out.println(A.getStr()+ " "+ Q.getStr()+" "+M.getStr()+" 第"+i+"周期");
} System.out.println(n1+"/"+n2 +" = "+ (isSameSign?Q.getInt():Q.getOppositeNumber().getInt())+"···"+A.getInt()); } public static void main(String[] args) {
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

												

二进制补码除法——计算机底层整数除法模拟之Java实现的更多相关文章

  1. ALU底层方法及计算机整数加减乘除模拟

    ALU是计算机CPU的核心,即 算术逻辑单元(arithmetic and logic unit)ALU有几大功能,是计算机计算最基础的功能:1.算术运算:包含加法.减法等2.逻辑运算:主要是布尔运算 ...

  2. 编译器是如何实现32位整型的常量整数除法优化的?[C/C++]

    引子 在我之前的一篇文章[ ThoughtWorks代码挑战——FizzBuzzWhizz游戏 通用高速版(C/C++ & C#) ]里曾经提到过编译器在处理除数为常数的除法时,是有优化的,今 ...

  3. int abs(int number)函数有感: 求补码和通过补码求对应的整数 C++(增加:数字的二进制表示中1的个数)

    #include "limits.h" #include "math.h" int abs(int number) { int const mask = num ...

  4. C/C++整数除法以及保留小数位的问题

    题目描述 Given two postive integers A and B,  please calculate the maximum integer C that C*B≤A, and the ...

  5. int float 的具体的取值范围取决于具体的机器 整数除法舍位 整形(int)也可以用于存储字符型数据

    int  通常为16位  存储单元 float  通常为32位 取决于具体的机器 #include main() { int fathr,celsius; int lower,upper,step; ...

  6. 【剑指 Offer II 001. 整数除法】同leedcode 29.两数相除

    剑指 Offer II 001. 整数除法 解题思路 在计算的时候将负数转化为正数,对于32位整数而言,最小的正数是-2^31, 将其转化为正数是2^31,导致溢出.因此将正数转化为负数不会导致溢出. ...

  7. c++ 超大整数除法 高精度除法

    c++ 超大整数除法 高精度除法 解题思路 计算a/b,其中a为大整数,b为普通整数,商为c,余数为r. 根据手算除法的规则,上一步的余数记为r,则本次计算的被除数为t=r*10+被除数的本位数值a[ ...

  8. 二进制补码:Why & How

    二进制补码:Why & How 学习计算机原理或者语言的底层操作难免会遇到用二进制补码表示负数的问题.由于一些书本上对于采用补码的原因没有详细解释,很多人会认为这只是一种规定,但实际上采用补码 ...

  9. Day05_C操作符及二进制补码计算

    回顾:  1.数据类型  2.二进制(八进制,十六进制) --------------------------------------------------------- 计算机中不可以使用负号表示 ...

随机推荐

  1. daemon_inetd函数

    #include <syslog.h> extern int daemon_proc; /* defined in error.c */ void daemon_inetd(const c ...

  2. mysql load_file在数据库注入中使用

    load_file函数只有满足两个条件就可以使用: 1.文件权限:chmod a+x pathtofile 2.文件大小: 必须小于max_allowed_packet 例子: select load ...

  3. C# WinForm开发系列 - Crystal Report水晶报表

    转自:ttp://www.cnblogs.com/peterzb/archive/2009/07/11/1521325.html 水晶报表(Crystal Report)是业内最专业.功能最强的报表系 ...

  4. sqlserver 备份脚本

    BACKUP DATABASE 数据库名称  TO DISK='d:\3333.bak' ---根据时间生成文件名 --将SQL脚本赋值给变量declare @SqlBackupDataBase as ...

  5. oracle 根据在线更新分区。

    LOG_PURCHASEINFO 是没有分区之前的表,根据 LOG_PURCHASEINFO_P 分区好的表在线更新 LOG_PURCHASEINFO表,让他变成分区表.11g才可以使用list_ra ...

  6. PHP WeBaCoo后门学习笔记

    PHP WeBaCoo后门学习笔记 - PHP WeBaCoo backdoor learning notes WeBaCoo (Web Backdoor Cookie) 是一款隐蔽的脚本类Web后门 ...

  7. Java SE之网络爬虫①

    一 需求描述 给一个url,将该url对应网页内的所有的链接查找出来,并补充完整为绝对路径 简易版 /** * * @author Zen Johnny * @date 2018年4月29日 下午11 ...

  8. CentOS7查询最近修改的文件

    当需要排查问题的时候,经常需要找到最近修改和产生的文件 下面的命令是查询当前目录下以log结尾的日志,并且在30分钟内修改过,这个可以根据情况修改时间为1分钟,查找最新产生的日志 突然想到这个问题,是 ...

  9. Hbase配置WEB UI界面

    1 找到各个节点下面的Hbase-site.xml文件,添加如下配置 <property> <name>hbase.master.info.port</name> ...

  10. Python之 string 和 random方法

    1. import string import string print(string.ascii_lowercase) #输出全部小写字母a-z print(string.ascii_letters ...