二进制补码除法——计算机底层整数除法模拟之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.二进制(八进制,十六进制) --------------------------------------------------------- 计算机中不可以使用负号表示 ...
随机推荐
- Asp.Net 初级 高级 学习笔记
01.Main函数是什么?在程序中使用Main函数有什么需要注意的地方?02.CLR是什么?程序集是什么?当运行一个程序集的时候,CLR做了什么事情?03.值类型的默认值是什么?(情况一:字段或全局静 ...
- sqlserver导入execl
一.找到导入导出的工具 找到安装目录 C:\Program Files\Microsoft SQL Server\100\DTS\Binn 里面的DTSWizard.exe 二.打开exe 然后下一步 ...
- kindeditor<=4.1.5 文件上传漏洞利用
kindeditor<=4.1.5 文件上传漏洞 - Kindeditor <=4.1.5 file upload vulnerability and use 漏洞存影响版本:小于等于4. ...
- luogu P3297 [SDOI2013]逃考
传送门 gugugu 首先每个人管理的区域是一个多边形,并且整个矩形是被这样的多边形填满的.现在的问题是求一条经过多边形最少的路径到达边界,这个可以最短路. 现在的问题是建图,显然我们应该给相邻的多边 ...
- Chain训练准则的计算
轮迭代时验证集的日志: log/compute_prob_valid.1000.log: LOG (nnet3-chain-compute-prob[5.5.100-d66be]:PrintTotal ...
- PhpStorm+xdebug+postman调试
PhpStorm+xdebug+postman调试 写PHP时,一直用postman做测试,最近发现在测试过程中可以用xdebug来断点调试,比原来手动打exit或者die来断点效率高多了. 下面记录 ...
- webpack 优化
1 优化loader配置 1.1 缩小文件匹配范围(include/exclude) 通过排除node_modules下的文件 从而缩小了loader加载搜索范围 高概率命中文件 module: ...
- Jmeter Md5加密操作之-------BeanShell PreProcessor
背景: 有一些登录会做一些md5校验,通过jmeter的BeanShell可以解决MD5加密情况. 1.首先需要一个解码的jar包,commons-codec-1.10.jar(网上很多),下载后,放 ...
- Python 爬虫六 性能相关
前面已经讲过了爬虫的两大基础模块: requests模块:用来伪造请求爬取数据 bs4模块:用来整理,提取数据 当我们真正的开始有需求的时候通常都是批量爬取url这样的.那如何批量爬取呢? 按照正常的 ...
- centos6.8安装python3.7无法import _ssl
转载https://www.jianshu.com/p/ace9be0b08ed 公司运维提供的服务器是centos6.8,打算在上面装python3.7,结果费尽周折,按照网上的步骤python3. ...