二进制补码除法——计算机底层整数除法模拟之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.二进制(八进制,十六进制) --------------------------------------------------------- 计算机中不可以使用负号表示 ...
随机推荐
- RIPS PHP源码静态分析(转)
0x00背景 对于PHP代码审计的需求,我们当然需要一款好的php代码审计分析工具--RIPS,它使用了静态分析技术,能够自动化地挖掘PHP源代码潜在的安全漏洞如XSS ,sql注入,敏感信息泄漏,文 ...
- luogu 3396 哈希冲突 奇怪的根号
这个题嘛开始一看实在想不出来有什么数据结构/算法可以乱搞,于是果断写了个朴素n方暴力,然后就发现luogu竟然有91分 这数据啊,也是醉了.. 想着优化优化能不能暴力卡过最后一个T掉的点,然鹅发现无耶 ...
- sql server 横向转丛向及FOR XML PATH使用
1.开始数据结构如下: 2.转为如下图: 使用如下SQL语句: ---横向转丛向 select name '姓名', max(case when course='语文' then score end) ...
- webpack中resolve用法
如果想在页面中使用 bootstrap 的样式,一般会在入口文件中引入 import 'bootstrap' 但是这样引入的 bootstrap 默认引入的是JS文件,所以页面的样式是不会生效的,下面 ...
- Hibernate持久化
一.主键生成策略 1)主键分类:1.自然主键:主键本身就是表中的某一个字段,实体中的一个具体属性,对象本身唯一的特性. 例如:创建一个学生,把其身份证号设为主键 2.代理主键:本身不是表中的一个必须的 ...
- Log4Net配置日志
1.log4net 1)新建一个Net空白项目,在引用出点击管理NuGet程序包,搜索log4net并安装 2)建立log4net.config配置文件 在configuration里面添加如下代码, ...
- Spring重温(四)--Spring自动组件扫描
通常情况下,声明所有的Bean类或组件的XML bean配置文件,这样Spring容器可以检测并注册Bean类或组件. 其实,Spring是能够自动扫描,检测和预定义的项目包并实例化bean,不再有繁 ...
- Thymeleaf在前台下拉列表获取后台传的值
Thymeleaf在前台下拉列表获取后台传的值 后台添加代码: /** * 新增机构 */ @GetMapping("/add") public String add(ModelM ...
- python,类和对象(二),self 、__init__(self,param[,param...])、__private(私有变量)
当我们在类中定义方法的时候,总会看到会第一个参数总是self,是的这个是python的一个语法,他相当于这个对象的指针. class Dog(): def setName(self,name): se ...
- Zookeeper客户端Curator基本API
在使用zookeper的时候一般不使用原生的API,Curator,解决了很多Zookeeper客户端非常底层的细节开发工作,包括连接重连.反复注册Watcher和NodeExistsExceptio ...