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

加法运算

  • 加法可以说是所有运算的基础, 有了加法,其他的减,乘, 除都可以用加法为基础进行
  • 废话不说, 直接lu代码, 涉及思路都写在代码注释里
  1. public class Addition {
  2. /**
  3. * 先sum, 后进位, 无进位,return sum
  4. * 使用递归进行加法计算
  5. *
  6. * 以13 + 9的8位二进举例
  7. * 00001101
  8. * + 00001001
  9. *-----------------
  10. * 00000100 :只求和(a ^ b)的结果
  11. * 00010010 :只求进位(a & b << 1)的结果;
  12. *------------------
  13. * 00010110 :对上面结果继续求和
  14. * 00000000 : 此时没有了进位,所以结果就是上面的sum
  15. * @param a
  16. * @param b
  17. * @return
  18. */
  19. public int sumRecursive(int a, int b){
  20. if(b == 0){
  21. return a;
  22. }else{
  23. return sumRecursive(a ^ b, (a & b) << 1);
  24. }
  25. }
  26. /**
  27. * 循环实现加法计算
  28. * @param a
  29. * @param b
  30. * @return
  31. */
  32. public int sumLoop(int a, int b){
  33. int sum = a;
  34. int carry = b;
  35. while (carry != 0){
  36. int temsum = sum;
  37. sum = sum ^ carry;
  38. carry = (temsum & carry) << 1;
  39. }
  40. return sum;
  41. }
  42. }
  43. public class TestAddition {
  44. @Test
  45. public void testAddition(){
  46. Addition addition = new Addition();
  47. Assert.assertEquals(22, addition.sumLoop(13, 9));
  48. Assert.assertEquals(22, addition.sumLoop(9, 13));
  49. Assert.assertEquals(22, addition.sumRecursive(13, 9));
  50. Assert.assertEquals(22, addition.sumRecursive(9, 13));
  51. }
  52. }

减法运算

  • 减法就是加法的逆运算
  • 一个正数的补码就等于它的相反数, 一个数求俩次补码还等于它自己
  1. public class Subtraction {
  2. /**
  3. * 减法就是加法的逆运算,
  4. * 原码 补码关系按照转换后10进制数来看 |原码| == |补码|
  5. *
  6. * 一个字节用数字9举例:
  7. * 二进制 10进制
  8. * 原码:00001001 9
  9. * 反码:11110110
  10. * 补码:11110111 -9
  11. *
  12. * 数字 -9
  13. * 原码: 11110111
  14. * 反码: 00001000
  15. * 补码: 00001001 -9的补码正好是9的原码
  16. * @param a
  17. * @param b
  18. * @return
  19. */
  20. public int subtraction(int a, int b){
  21. if(b == 0){
  22. return a;
  23. }else {
  24. Addition addition = new Addition();
  25. return addition.sumRecursive(a, addition.sumRecursive(~b, 1));
  26. }
  27. }
  28. }
  29. public class TestSubtraction {
  30. @Test
  31. public void testSubtraction(){
  32. int a = 22;
  33. int b = 9;
  34. Subtraction subtraction = new Subtraction();
  35. Assert.assertEquals(13,subtraction.subtraction(a,b));
  36. }
  37. }

乘法运算

  • 乘法运算这里写了俩种方式, 一种(v1版本)直男写的, 耿直性能低, 另一种烧脑, 时间短
  • 俩数做异或就可以确定运算符号 是 + 还是 -
  • 一个正数 &0x1 只能是0或者是1, 来确定当前二进位是0还是1
  1. public class Multiplication {
  2. /**
  3. * 第一感觉, 乘法就是多次的加法, 将相同的数多次累计求和
  4. * 需要考虑运算符号, 通过俩数异或判断
  5. * @param a
  6. * @param b
  7. * @return
  8. */
  9. public int multiplicationV1(int a, int b){
  10. Addition addition = new Addition();
  11. //异或小于0, 则结果为负
  12. boolean flag = ((a ^ b) < 0);
  13. //对a, b求绝对值
  14. a = a > 0 ? a:addition.sumRecursive(~a, 1);
  15. b = b > 0 ? b:addition.sumRecursive(~b, 1);
  16. int sum = 0;
  17. for (int i = 0; i < b; i++) {
  18. //b 数量级很大时, 循环次数太多
  19. sum = addition.sumRecursive(addition.sumRecursive(0, a), sum);
  20. }
  21. if(flag){
  22. //通过判断符号位, 将绝对值转换为指定数
  23. return addition.sumRecursive(~sum, 1) ;
  24. }else{
  25. return sum;
  26. }
  27. }
  28. /**
  29. * 使用算法规则
  30. * 0100 4
  31. * x 1001 9
  32. * 100100 36
  33. *
  34. * 计算过程
  35. * 0100
  36. * x 1001
  37. * -----------
  38. * 判断 参数1 > 0: 参数1(偶数:4) & 0x1 == 0, 参数1右移动0010, 参数2左移(10010)
  39. * 判断 参数1 > 0: 参数1(偶数: 2) & 0x1 == 0, 参数1右移动0001, 参数2左移(100100)
  40. * 判断 参数1 > 0: 参数1(基数:1) & 0x1 == 1, 满足(if), sum累加当前参数2, sum(100100), 参数1右移动0000, 参数2左移(1001000)
  41. * 判断 参数1 !> 0, 跳出, 返回结果
  42. *
  43. *
  44. * 需要了解:
  45. * 偶数 & 0x1 == 0
  46. * 奇数 & 0x1 == 1
  47. * a^b < 0 一定符号相反
  48. *
  49. * 只要参数1不等于0就一直循环, 每次循环参数2左移一位并赋值给参数2
  50. * 如果参数1当前位置为1, 那么将当前参数2的值累加
  51. *
  52. * @param a
  53. * @param b
  54. * @return
  55. */
  56. public int multiplicationV2(int a, int b){
  57. Addition addition = new Addition();
  58. //异或小于0, 则结果为负
  59. boolean flag = ((a ^ b) < 0);
  60. //对a, b求绝对值
  61. a = a > 0 ? a:addition.sumRecursive(~a, 1);
  62. b = b > 0 ? b:addition.sumRecursive(~b, 1);
  63. int sum = 0;
  64. while(a > 0){
  65. if((a&0x1) != 0){
  66. //如果参数1,当前位置是1, 将目前的b累加sum
  67. sum = addition.sumRecursive(b,sum);
  68. }
  69. //每次循环参数2左移位
  70. b = b<<1;
  71. //第一个参数一直右移来判断当前是否为0
  72. a = a>>1;
  73. }
  74. if(flag){
  75. //通过判断符号位, 将绝对值转换为指定数
  76. return addition.sumRecursive(~sum, 1) ;
  77. }else{
  78. return sum;
  79. }
  80. }
  81. }
  82. public class TestMultiplication {
  83. private static final Multiplication multiplication = new Multiplication();
  84. @Test
  85. public void testMultiplicationV1(){
  86. Assert.assertEquals(-6, multiplication.multiplicationV1(-2, 3));
  87. Assert.assertEquals(-6, multiplication.multiplicationV1(-3, 2));
  88. Assert.assertEquals(6, multiplication.multiplicationV1(3, 2));
  89. Assert.assertEquals(6, multiplication.multiplicationV1(6, 1));
  90. }
  91. @Test
  92. public void testMultiplicationV2(){
  93. Assert.assertEquals(-6, multiplication.multiplicationV2(-2, 3));
  94. Assert.assertEquals(-6, multiplication.multiplicationV2(-3, 2));
  95. Assert.assertEquals(6, multiplication.multiplicationV2(3, 2));
  96. Assert.assertEquals(6, multiplication.multiplicationV2(6, 1));
  97. }
  98. @Test
  99. public void testPerformance(){
  100. long l = System.currentTimeMillis();
  101. System.out.println(multiplication.multiplicationV1(2, 999999999));
  102. System.out.println(System.currentTimeMillis() - l);//3764
  103. //第二种性能完虐第一种
  104. long l2 = System.currentTimeMillis();
  105. System.out.println(multiplication.multiplicationV2(2, 999999999));
  106. System.out.println(System.currentTimeMillis() - l2);//1
  107. }
  108. }

除法运算

  • 同样除法可以看作是多次求减, 但是可能会有余数, 也有俩个版本, 直男版(v1)和动脑子版(v2)
  1. public class Division {
  2. private static final Addition addition = new Addition();
  3. private static final Subtraction subtraction = new Subtraction();
  4. /**
  5. * 同样先来使用最脑残的方式来计算商
  6. * <p>
  7. * 商跟乘积相反, 就是用除数b不断的去扣减被除数a, 直到a < b, 这时循环次数就是商, b-a的值就是余数
  8. *
  9. * @param a
  10. * @param b
  11. * @return
  12. */
  13. public int divisionV1(int a, int b) {
  14. //是否负数
  15. boolean islowthanzero = (a ^ b) < 0;
  16. //绝对值
  17. a = (a > 0 ? a : addition.sumRecursive(~a, 1));
  18. b = (b > 0 ? b : addition.sumRecursive(~b, 1));
  19. Subtraction subtraction = new Subtraction();
  20. int remainder = 0;
  21. int quotient = 0;
  22. int count = 0;
  23. while (a > b) {
  24. remainder = subtraction.subtraction(a, b);
  25. a = subtraction.subtraction(a, b);
  26. quotient++;
  27. count++;
  28. }
  29. System.out.println("次数:" + count);
  30. if (islowthanzero) {
  31. return addition.sumRecursive(~quotient, 1);
  32. } else {
  33. return quotient;
  34. }
  35. }
  36. /**
  37. * v1版本如果a极大, 而b极小, 会找成循环次数超多, 所以要从一个大值开始除, int类型最大值为2^32次, 如果能a除2^n次后还大于b, 那么商就是2^n次
  38. * 该方式性能完虐v1
  39. * @param a
  40. * @param b
  41. * @return
  42. */
  43. public int divisionV2(int a, int b) {
  44. //是否负数
  45. boolean islowthanzero = (a ^ b) < 0;
  46. //绝对值
  47. a = (a > 0 ? a : addition.sumRecursive(~a, 1));
  48. b = (b > 0 ? b : addition.sumRecursive(~b, 1));
  49. int remainder = 0;
  50. int quotient = 0;
  51. //int 类型数除 2^32次都等于原数字, 所以如果i == 32循环次数还是与b有关, 与v1 相同
  52. for (int i = 31; i >= 0; i--) {
  53. while (a >> i >= b) {
  54. remainder = subtraction.subtraction(a, b);
  55. a = subtraction.subtraction(a, b << i);
  56. quotient = addition.sumRecursive(1 << i, quotient);
  57. }
  58. }
  59. if (islowthanzero) {
  60. return addition.sumRecursive(~quotient, 1);
  61. } else {
  62. return quotient;
  63. }
  64. }
  65. }
  66. public class TestDivision {
  67. private static final Division division = new Division();
  68. @Test
  69. public void testDivisionV1(){
  70. Assert.assertEquals(2,division.divisionV1(5, 2));
  71. }
  72. @Test
  73. public void testDivisionV2(){
  74. Assert.assertEquals(-33,division.divisionV2(-100, 3));
  75. }
  76. @Test
  77. public void testDivisionPerformance(){
  78. // long l = System.currentTimeMillis();
  79. // System.out.println(division.divisionV1(999999999, 1));
  80. // System.out.println(System.currentTimeMillis() - l);//21428
  81. //第二种性能完虐第一种
  82. long l2 = System.currentTimeMillis();
  83. System.out.println(division.divisionV2(999999999, 1));
  84. System.out.println(System.currentTimeMillis() - l2);//1
  85. }
  86. }

代码路径:

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. Window: move\copy\xcopy

    Move 移动文件和重命名文件与目录. 要移动一个或多个文件: MOVE [/Y | /-Y] [drive:][path]filename1[,...] destination 要重命名目录: MO ...

  2. 后台注册js代码的方法

    Page.ClientScript.RegisterClientScriptBlock(JSUtil.CurrentPage.GetType(), Util.NewGuid(), @"< ...

  3. Microsoft(C)注册服务器(32位)CPU占用高

    Microsoft(C)注册服务器(32位)CPU占用高 摘自:https://blog.csdn.net/jtsqrj/article/details/83034252 2018年10月12日 23 ...

  4. 在linux下使用CMake构建应用程序

    本文介绍了一个跨平台的自动化构建系统 CMake 在 linux 上的使用方法. CMake 是一个比 automake 更加容易使用的工具,能够使程序员从复杂的编译连接过程中解脱出来.文中通过一些例 ...

  5. AngularJs(v1)相关知识和经验的碎片化记录

    1.利用angular指令监听ng-repeat渲染完成后执行脚本 http://www.cnblogs.com/wangmeijian/p/5141266.html 2.$http的POST请求中请 ...

  6. 白盒测试实践--Day2

    累计完成任务情况: 阶段内容 参与人 完成CheckStyle检查 小靳 完成代码评审会议纪要和结果报告 小熊.小梁及其他 完成白盒测试用例 小尹 学习静态代码审核,确定评审表,开评审会确定高危区代码 ...

  7. 白盒测试实践--Day0

    白盒测试实践--Day0 累计完成任务情况: 阶段内容 参与人 开会学习作业要求,取得共识 全体 注: 1."阶段内容"划斜线表示完成. 2.采用倒序. 具体情况: 组长提前组织分 ...

  8. javascript总结28 :匿名函数

    1 匿名函数 //匿名函数. // (function (){ // console.log(1); // }) 2  匿名函数作用 //1.直接调用 (function (){ console.lo ...

  9. CodeForces 347B Fixed Points (水题)

    题意:给定 n 数,让你交换最多1次,求满足 ai = i的元素个数. 析:很简单么,只要暴力一遍就OK了,先把符合的扫出来,然后再想,最多只能交换一次,也就是说最多也就是加两个,然后一个的判,注意数 ...

  10. Eclipse连接数据库

    原创 操作数据库之前首先得连接数据库,连接数据库的步骤如下: 将驱动包导入JDK中 将sqljdbc4.jar(一个举例)类库文件拷贝到D:\Program Files\Java\jdk1.7.0\j ...