很多语言底层对四则运算都有内部封装, 我们还是要重复造下轮子,不为别的, 就是为了面试可以多装一分 b, 假设你已经了解了什么是二进制, 什么是异或, 什么是移位运算, 什么是与, 这些不懂就别硬上(先区了解下),小心走火入魔

加法运算

  • 加法可以说是所有运算的基础, 有了加法,其他的减,乘, 除都可以用加法为基础进行
  • 废话不说, 直接lu代码, 涉及思路都写在代码注释里
public class Addition {
/**
* 先sum, 后进位, 无进位,return sum
* 使用递归进行加法计算
*
* 以13 + 9的8位二进举例
* 00001101
* + 00001001
*-----------------
* 00000100 :只求和(a ^ b)的结果
* 00010010 :只求进位(a & b << 1)的结果;
*------------------
* 00010110 :对上面结果继续求和
* 00000000 : 此时没有了进位,所以结果就是上面的sum
* @param a
* @param b
* @return
*/
public int sumRecursive(int a, int b){
if(b == 0){
return a;
}else{
return sumRecursive(a ^ b, (a & b) << 1);
}
} /**
* 循环实现加法计算
* @param a
* @param b
* @return
*/
public int sumLoop(int a, int b){
int sum = a;
int carry = b;
while (carry != 0){
int temsum = sum;
sum = sum ^ carry;
carry = (temsum & carry) << 1;
}
return sum;
}
}
public class TestAddition {
@Test
public void testAddition(){
Addition addition = new Addition();
Assert.assertEquals(22, addition.sumLoop(13, 9));
Assert.assertEquals(22, addition.sumLoop(9, 13)); Assert.assertEquals(22, addition.sumRecursive(13, 9));
Assert.assertEquals(22, addition.sumRecursive(9, 13));
}
}

减法运算

  • 减法就是加法的逆运算
  • 一个正数的补码就等于它的相反数, 一个数求俩次补码还等于它自己
public class Subtraction {

    /**
* 减法就是加法的逆运算,
* 原码 补码关系按照转换后10进制数来看 |原码| == |补码|
*
* 一个字节用数字9举例:
* 二进制 10进制
* 原码:00001001 9
* 反码:11110110
* 补码:11110111 -9
*
* 数字 -9
* 原码: 11110111
* 反码: 00001000
* 补码: 00001001 -9的补码正好是9的原码
* @param a
* @param b
* @return
*/
public int subtraction(int a, int b){
if(b == 0){
return a;
}else {
Addition addition = new Addition();
return addition.sumRecursive(a, addition.sumRecursive(~b, 1));
}
}
}
public class TestSubtraction {
@Test
public void testSubtraction(){
int a = 22;
int b = 9;
Subtraction subtraction = new Subtraction();
Assert.assertEquals(13,subtraction.subtraction(a,b));
}
}

乘法运算

  • 乘法运算这里写了俩种方式, 一种(v1版本)直男写的, 耿直性能低, 另一种烧脑, 时间短
  • 俩数做异或就可以确定运算符号 是 + 还是 -
  • 一个正数 &0x1 只能是0或者是1, 来确定当前二进位是0还是1
public class Multiplication {

    /**
* 第一感觉, 乘法就是多次的加法, 将相同的数多次累计求和
* 需要考虑运算符号, 通过俩数异或判断
* @param a
* @param b
* @return
*/
public int multiplicationV1(int a, int b){
Addition addition = new Addition(); //异或小于0, 则结果为负
boolean flag = ((a ^ b) < 0); //对a, b求绝对值
a = a > 0 ? a:addition.sumRecursive(~a, 1);
b = b > 0 ? b:addition.sumRecursive(~b, 1);
int sum = 0;
for (int i = 0; i < b; i++) {
//b 数量级很大时, 循环次数太多
sum = addition.sumRecursive(addition.sumRecursive(0, a), sum);
} if(flag){
//通过判断符号位, 将绝对值转换为指定数
return addition.sumRecursive(~sum, 1) ;
}else{
return sum;
}
} /**
* 使用算法规则
* 0100 4
* x 1001 9
* 100100 36
*
* 计算过程
* 0100
* x 1001
* -----------
* 判断 参数1 > 0: 参数1(偶数:4) & 0x1 == 0, 参数1右移动0010, 参数2左移(10010)
* 判断 参数1 > 0: 参数1(偶数: 2) & 0x1 == 0, 参数1右移动0001, 参数2左移(100100)
* 判断 参数1 > 0: 参数1(基数:1) & 0x1 == 1, 满足(if), sum累加当前参数2, sum(100100), 参数1右移动0000, 参数2左移(1001000)
* 判断 参数1 !> 0, 跳出, 返回结果
*
*
* 需要了解:
* 偶数 & 0x1 == 0
* 奇数 & 0x1 == 1
* a^b < 0 一定符号相反
*
* 只要参数1不等于0就一直循环, 每次循环参数2左移一位并赋值给参数2
* 如果参数1当前位置为1, 那么将当前参数2的值累加
*
* @param a
* @param b
* @return
*/
public int multiplicationV2(int a, int b){
Addition addition = new Addition(); //异或小于0, 则结果为负
boolean flag = ((a ^ b) < 0); //对a, b求绝对值
a = a > 0 ? a:addition.sumRecursive(~a, 1);
b = b > 0 ? b:addition.sumRecursive(~b, 1); int sum = 0;
while(a > 0){
if((a&0x1) != 0){
//如果参数1,当前位置是1, 将目前的b累加sum
sum = addition.sumRecursive(b,sum);
} //每次循环参数2左移位
b = b<<1;
//第一个参数一直右移来判断当前是否为0
a = a>>1;
} if(flag){
//通过判断符号位, 将绝对值转换为指定数
return addition.sumRecursive(~sum, 1) ;
}else{
return sum;
}
}
} public class TestMultiplication { private static final Multiplication multiplication = new Multiplication();
@Test
public void testMultiplicationV1(){
Assert.assertEquals(-6, multiplication.multiplicationV1(-2, 3));
Assert.assertEquals(-6, multiplication.multiplicationV1(-3, 2));
Assert.assertEquals(6, multiplication.multiplicationV1(3, 2));
Assert.assertEquals(6, multiplication.multiplicationV1(6, 1)); } @Test
public void testMultiplicationV2(){
Assert.assertEquals(-6, multiplication.multiplicationV2(-2, 3));
Assert.assertEquals(-6, multiplication.multiplicationV2(-3, 2));
Assert.assertEquals(6, multiplication.multiplicationV2(3, 2));
Assert.assertEquals(6, multiplication.multiplicationV2(6, 1));
} @Test
public void testPerformance(){
long l = System.currentTimeMillis();
System.out.println(multiplication.multiplicationV1(2, 999999999));
System.out.println(System.currentTimeMillis() - l);//3764 //第二种性能完虐第一种
long l2 = System.currentTimeMillis();
System.out.println(multiplication.multiplicationV2(2, 999999999));
System.out.println(System.currentTimeMillis() - l2);//1
}
}

除法运算

  • 同样除法可以看作是多次求减, 但是可能会有余数, 也有俩个版本, 直男版(v1)和动脑子版(v2)
public class Division {

    private static final Addition addition = new Addition();

    private static final Subtraction subtraction = new Subtraction();

    /**
* 同样先来使用最脑残的方式来计算商
* <p>
* 商跟乘积相反, 就是用除数b不断的去扣减被除数a, 直到a < b, 这时循环次数就是商, b-a的值就是余数
*
* @param a
* @param b
* @return
*/
public int divisionV1(int a, int b) { //是否负数
boolean islowthanzero = (a ^ b) < 0; //绝对值
a = (a > 0 ? a : addition.sumRecursive(~a, 1));
b = (b > 0 ? b : addition.sumRecursive(~b, 1)); Subtraction subtraction = new Subtraction();
int remainder = 0;
int quotient = 0;
int count = 0;
while (a > b) {
remainder = subtraction.subtraction(a, b);
a = subtraction.subtraction(a, b);
quotient++;
count++;
}
System.out.println("次数:" + count); if (islowthanzero) {
return addition.sumRecursive(~quotient, 1);
} else {
return quotient;
}
} /**
* v1版本如果a极大, 而b极小, 会找成循环次数超多, 所以要从一个大值开始除, int类型最大值为2^32次, 如果能a除2^n次后还大于b, 那么商就是2^n次
* 该方式性能完虐v1
* @param a
* @param b
* @return
*/
public int divisionV2(int a, int b) {
//是否负数
boolean islowthanzero = (a ^ b) < 0; //绝对值
a = (a > 0 ? a : addition.sumRecursive(~a, 1));
b = (b > 0 ? b : addition.sumRecursive(~b, 1)); int remainder = 0;
int quotient = 0; //int 类型数除 2^32次都等于原数字, 所以如果i == 32循环次数还是与b有关, 与v1 相同
for (int i = 31; i >= 0; i--) {
while (a >> i >= b) {
remainder = subtraction.subtraction(a, b);
a = subtraction.subtraction(a, b << i);
quotient = addition.sumRecursive(1 << i, quotient);
}
} if (islowthanzero) {
return addition.sumRecursive(~quotient, 1);
} else {
return quotient;
}
}
} public class TestDivision { private static final Division division = new Division();
@Test
public void testDivisionV1(){
Assert.assertEquals(2,division.divisionV1(5, 2));
} @Test
public void testDivisionV2(){
Assert.assertEquals(-33,division.divisionV2(-100, 3));
} @Test
public void testDivisionPerformance(){
// long l = System.currentTimeMillis();
// System.out.println(division.divisionV1(999999999, 1));
// System.out.println(System.currentTimeMillis() - l);//21428 //第二种性能完虐第一种
long l2 = System.currentTimeMillis();
System.out.println(division.divisionV2(999999999, 1));
System.out.println(System.currentTimeMillis() - l2);//1
}
}

代码路径:

https://github.com/offline7LY/lintcoderoad/tree/master/src/main/java/com/lx/lintcoderoad/operator

参考:

http://www.cnblogs.com/kiven-code/archive/2012/09/15/2686922.html

https://www.jianshu.com/p/7bba031b11e7

水平有限,希望帮到大家

如何实现java的四则运算的更多相关文章

  1. Java实现四则运算,使用堆栈,检查语法

    突然发闲想试一试自己实现算术的四则运算,支持加减乘除和括号.正负号:支持语法检查:思路很常规,利用两个堆栈,一个压操作符,一个压操作数,念头冒出来之后,立马动手:然后本以为很容易的一个实现,却存在各种 ...

  2. 结对编程1----基于java的四则运算生成器

    小组成员:王震(201421123054).王杰(201421123055) Coding地址:https://git.coding.net/a506504661/sssss.git 一.题目描述 我 ...

  3. java重构四则运算

    package 重构四则运算; import java.io.IOException; public class Test { public static void main(String[] arg ...

  4. 结对编程——paperOne基于java的四则运算 功能改进

    项目成员:张金生     张政 由于新的需求,原本使用JSP的实现方式目前改为Java实现,即去除了B/S端. 需求分析: 1.四则运算要满足整数运算.分数运算两种: 2.运算题目随机,并且可以打印题 ...

  5. java小学生四则运算带面板版 但我不知道为什么同类变量却进不了动作监听中去

    ---恢复内容开始--- package yun; import java.util.*; import java.awt.*; import java.awt.event.ActionEvent; ...

  6. JAVA实现四则运算的简单计算器

    开发环境eclipse java neon. 今天用JAVA的swing界面程序设计制作了一个四则运算的简易计算器.代码以及截图如下: computer.java: ///////////////// ...

  7. Java 执行四则运算

    四种基本的操作原理是将被转换成后缀缀表达式表达.然后计算. 转换思路和原则.可以参考将中缀表达式转化为后缀表达式 import java.math.BigDecimal; import java.ut ...

  8. java实现四则运算应用(基于控制台)

    项目地址:https://gitee.com/wxrqforever/object_oriented_exp1.git 一.需求分析: 一个基于控制台的四则运算系统,要能实现生成并计算含有真,假分数, ...

  9. java 高精度 四则运算

    java的大数处理对于ACM中的大数来说,相当的简单啊: 整数的运算   BigInteger 小数的运算   BigDecimal 导入类: import java.util.Scanner; im ...

随机推荐

  1. Netty之Reactor模式

    无论是C++还是Java编写的网络框架,大多数都是基于Reactor模式进行设计和开发,Reactor模式基于事件驱动,特别适合处理海量的I/O事件. 1. 单线程模型 Reactor单线程模型,指的 ...

  2. 3.1.7 线程阻塞工具类:LockSupport

    package 第三章.线程阻塞工具LockSupport; import java.util.concurrent.locks.LockSupport; /** * Created by zzq o ...

  3. 免秘钥oracel官方下载jdk

    wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-co ...

  4. Spring框架总结(三)

    SpringIOC容器 一.创建对象 SpringIOC容器,是spring核心内容. 作用: 创建对象 & 处理对象的依赖关系 IOC容器创建对象: 创建对象, 有几种方式: 1) 调用无参 ...

  5. js教程系列32 :javascript-DOM节点操作

    1 DOM节点操作 1.1 创建节点 createElement() var createDiv = document.createElement("div"); 1.2 插入子节 ...

  6. up6-自定义文件存储路径

    在up6.2中有两种保存模式,一种是md5一种是uuid. md5由PathMd5Builder生成存储路径.md5主要提供给文件使用,可在服务器端保存唯一的文件,有效避免重复文件. uuid由Pat ...

  7. Git出现SSL connect error的解决办法

    最近在使用Go开发项目中,因为需要安装一个Package,所以使用了go get命令 ,然后就出现git clone ... SSL connect error的错误: 出现这种错误有可能是nss的版 ...

  8. @cms_content_list

    [@cms_content_list typeId='1,2,3' count='18' orderBy='4' channelId='75' channelOption='0' dateFormat ...

  9. jsp乱码的问题

    大家在JSP的开发过程中,经常出现中文乱码的问题,可能一至困扰着大家,现把JSP开发中遇到的中文乱码的问题及解决办法写出来供大家参考.首先了解一下Java中文问题的由来: Java的内核和class文 ...

  10. json的两种格式

    JSON: JavaScript Object Notation      (JavaScript 对象表示法) JSON 是存储和交换文本信息的语法.类似 XML. 一.JSON对象:JSONObj ...