很多语言底层对四则运算都有内部封装, 我们还是要重复造下轮子,不为别的, 就是为了面试可以多装一分 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. Part2_lesson3---ARM寄存器详解

    进入到ARM Architecture Reference Manual这个文档里面的A2.3 Registers R13在程序中通常用于充当SP堆栈指针的!! R14在程序当中通常用于充当LR(链接 ...

  2. vmware workstation 12 密钥

    VMware Workstation 12序列号:5A02H-AU243-TZJ49-GTC7K-3C61N

  3. Perl语言编程>>学习笔记

    1. 使用反引号可以调用外部程序并返回程序的输出, 如  $cwd = `pwd`; 2. Perl 中的变量类型之间的区别主要是单数和复数; 单数变量称为标量 $scalar , 复数变量称为数组 ...

  4. Python3+Apache+Django+CentOS

    使用django开发的项目上到正式环境的环境搭建,系统软件版本: CentOS6. setuptools-.tar.gz pip-.tar.gz Python-.tgz pcre-8.39.tar.b ...

  5. 编写高质量代码改善C#程序的157个建议——建议123:程序集不必与命名空间同名

    建议123:程序集不必与命名空间同名 程序集一般会和命名空间同名,但这并不是必须的.事实上,不同名的命名空间和程序集是很常见的. 程序集表示的是一种物理上的分组,而命名空间是逻辑上的分组,两者没有必然 ...

  6. 深入理解java虚拟机(四)垃圾收集算法及HotSpot实现

    垃圾收集算法 一般来说,垃圾收集算法分为四类: 标记-清除算法 最基础的算法便是标记-清除算法(Mark-Sweep).算法分为“标记”和“清除”两个阶段:首先标记处需要收集的对象,在标记完成之后,再 ...

  7. 【小梅哥FPGA进阶教程】第十三章 四通道数字电压表

    十三.四通道数字电压表 本文由山东大学研友袁卓贡献,特此感谢 实验目的 设计一个四通道的数字电压表 实验平台 芯航线FPGA核心板.AD/DA模块 实验现象 实现一个四通道的数字电压表,其中可以用按键 ...

  8. Mysql自动设置时间(自动获取时间,填充时间)

    应用场景: 1.在数据表中,要记录每条数据是什么时候创建的,不需要应用程序去特意记录,而由数据数据库获取当前时间自动记录创建时间: 2.在数据库中,要记录每条数据是什么时候修改的,不需要应用程序去特意 ...

  9. Python-面向对象编程01_什么是面向对象

    Python从设计之初就已经是一门面向对象的语言了,正因如此,在Python中创建一个类和对象是很容易的. 什么是面向对象? 面向对象程序设计(Object-oriented programming, ...

  10. 小试maven工程

    由于工作中要用到maven来进行开发j2ee开发,所以选用了集成maven的eclipse版本: 下载地址: https://www.eclipse.org/downloads/ 根据提示下载32或者 ...