二进制补码除法——计算机底层整数除法模拟之Java实现
前面讲到布思算法的计算机底层模拟的时候,我们是借助于一个可以储存、表示任意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实现的更多相关文章
- ALU底层方法及计算机整数加减乘除模拟
ALU是计算机CPU的核心,即 算术逻辑单元(arithmetic and logic unit)ALU有几大功能,是计算机计算最基础的功能:1.算术运算:包含加法.减法等2.逻辑运算:主要是布尔运算 ...
- 编译器是如何实现32位整型的常量整数除法优化的?[C/C++]
引子 在我之前的一篇文章[ ThoughtWorks代码挑战——FizzBuzzWhizz游戏 通用高速版(C/C++ & C#) ]里曾经提到过编译器在处理除数为常数的除法时,是有优化的,今 ...
- int abs(int number)函数有感: 求补码和通过补码求对应的整数 C++(增加:数字的二进制表示中1的个数)
#include "limits.h" #include "math.h" int abs(int number) { int const mask = num ...
- C/C++整数除法以及保留小数位的问题
题目描述 Given two postive integers A and B, please calculate the maximum integer C that C*B≤A, and the ...
- int float 的具体的取值范围取决于具体的机器 整数除法舍位 整形(int)也可以用于存储字符型数据
int 通常为16位 存储单元 float 通常为32位 取决于具体的机器 #include main() { int fathr,celsius; int lower,upper,step; ...
- 【剑指 Offer II 001. 整数除法】同leedcode 29.两数相除
剑指 Offer II 001. 整数除法 解题思路 在计算的时候将负数转化为正数,对于32位整数而言,最小的正数是-2^31, 将其转化为正数是2^31,导致溢出.因此将正数转化为负数不会导致溢出. ...
- c++ 超大整数除法 高精度除法
c++ 超大整数除法 高精度除法 解题思路 计算a/b,其中a为大整数,b为普通整数,商为c,余数为r. 根据手算除法的规则,上一步的余数记为r,则本次计算的被除数为t=r*10+被除数的本位数值a[ ...
- 二进制补码:Why & How
二进制补码:Why & How 学习计算机原理或者语言的底层操作难免会遇到用二进制补码表示负数的问题.由于一些书本上对于采用补码的原因没有详细解释,很多人会认为这只是一种规定,但实际上采用补码 ...
- Day05_C操作符及二进制补码计算
回顾: 1.数据类型 2.二进制(八进制,十六进制) --------------------------------------------------------- 计算机中不可以使用负号表示 ...
随机推荐
- NFine框架全选checkBox列错位
在jqgrid.css里找到 .ui-jqgrid .cbox{margin-left: -1px;position: initial;vertical-align: text-bottom;}.ui ...
- luogu P3722 [AH2017/HNOI2017]影魔
传送门 我太弱了,只会乱搞,正解是不可能正解的,这辈子不可能写正解的,太蠢了又想不出什么东西,就是乱搞这种东西,才能维持得了做题这样子 考虑将询问离线,按右端点排序,并且预处理出每个位置往前面第一个大 ...
- NFS网络共享文件系统
1.nfs服务端配置操作 1.1 创建所需的共享目录--源 mkdir /data/rw #rw代表同步的数据可读可写 1.2 对共享目录进行授权 chown -R nfsnobody.nfsno ...
- Vue中的双向数据绑定简单介绍
1. 文本框绑定v-module <div id="app"> <input type="text" v-model="msg&qu ...
- Menu显示三个点,不显示内容
先说下menu的使用 首先自定义一个menu选项 <menu xmlns:android="http://schemas.android.com/apk/res/android&quo ...
- mysql 原理 ~ 死锁问题
一 锁1 锁的定义 1 按照宏观角度 共享锁[S锁] 又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的 ...
- Django学习手册 - 创建Django工程项目以及APP
前置步骤: 下载python,django 并且安装好 python 解释器以及django模块. 整体步骤阐述: 创建django工程项目 步骤一:进入安装的python目录 步骤二:输入创建工程的 ...
- python print输出带颜色 总结
书写格式: 开头部分:\033[显示方式;前景色;背景色m + 结尾部分:\033[0m 注意:开头部分的三个参数:显示方式,前景色,背景色是可选参数,可以只写其中的某一个:另外由 ...
- 【转】python之配置日志的几种方式
[转]python之配置日志的几种方式 作为开发者,我们可以通过以下3种方式来配置logging: 1)使用Python代码显式的创建loggers, handlers和formatters并分别调用 ...
- 旷视科技 -- Face++ 世界最大的人脸识别技术平台
旷视科技 -- Face++ 世界最大的人脸识别技术平台: https://www.megvii.com/