Java计算数学表达式代码详解

Java字符串转换成算术表达式计算并输出结果,通过这个工具可以直接对字符串形式的算术表达式进行运算,并且使用非常简单。

这个工具中包含两个类 Calculator 和 ArithHelper

Calculator 代码如下:

import java.util.Collections;
import java.util.Stack;
/**
 * 算数表达式求值
 * 直接调用Calculator的类方法conversion()
 * 传入算数表达式,将返回一个浮点值结果
 * 如果计算过程错误,将返回一个NaN
 */
public class Calculator {
	private Stack<String> postfixStack = new Stack<String>();
	// 后缀式栈
	private Stack<Character> opStack = new Stack<Character>();
	// 运算符栈
	private int[] operatPriority = new int[] { 0, 3, 2, 1, -1, 1, 0, 2 };
	// 运用运算符ASCII码-40做索引的运算符优先级
	public static double conversion(String expression) {
		double result = 0;
		Calculator cal = new Calculator();
		try {
			expression = transform(expression);
			result = cal.calculate(expression);
		}
		catch (Exception e) {
			// e.printStackTrace();
			// 运算错误返回NaN
			return 0.0 / 0.0;
		}
		// return new String().valueOf(result);
		return result;
	}
	/**
   * 将表达式中负数的符号更改
   *
   * @param expression
   *      例如-2+-1*(-3E-2)-(-1) 被转为 ~2+~1*(~3E~2)-(~1)
   * @return
   */
	private static String transform(String expression) {
		char[] arr = expression.toCharArray();
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] == '-') {
				if (i == 0) {
					arr[i] = '~';
				} else {
					char c = arr[i - 1];
					if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == 'E' || c == 'e') {
						arr[i] = '~';
					}
				}
			}
		}
		if(arr[0]=='~'||arr[1]=='('){
			arr[0]='-';
			return "0"+new String(arr);
		} else{
			return new String(arr);
		}
	}
	/**
   * 按照给定的表达式计算
   *
   * @param expression
   *      要计算的表达式例如:5+12*(3+5)/7
   * @return
   */
	public double calculate(String expression) {
		Stack<String> resultStack = new Stack<String>();
		prepare(expression);
		Collections.reverse(postfixStack);
		// 将后缀式栈反转
		String firstValue, secondValue, currentValue;
		// 参与计算的第一个值,第二个值和算术运算符
		while (!postfixStack.isEmpty()) {
			currentValue = postfixStack.pop();
			if (!isOperator(currentValue.charAt(0))) {
				// 如果不是运算符则存入操作数栈中
				currentValue = currentValue.replace("~", "-");
				resultStack.push(currentValue);
			} else {
				// 如果是运算符则从操作数栈中取两个值和该数值一起参与运算
				secondValue = resultStack.pop();
				firstValue = resultStack.pop();
				// 将负数标记符改为负号
				firstValue = firstValue.replace("~", "-");
				secondValue = secondValue.replace("~", "-");
				String tempResult = calculate(firstValue, secondValue, currentValue.charAt(0));
				resultStack.push(tempResult);
			}
		}
		return double.valueOf(resultStack.pop());
	}
	/**
   * 数据准备阶段将表达式转换成为后缀式栈
   *
   * @param expression
   */
	private void prepare(String expression) {
		opStack.push(',');
		// 运算符放入栈底元素逗号,此符号优先级最低
		char[] arr = expression.toCharArray();
		int currentIndex = 0;
		// 当前字符的位置
		int count = 0;
		// 上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值
		char currentOp, peekOp;
		// 当前操作符和栈顶操作符
		for (int i = 0; i < arr.length; i++) {
			currentOp = arr[i];
			if (isOperator(currentOp)) {
				// 如果当前字符是运算符
				if (count > 0) {
					postfixStack.push(new String(arr, currentIndex, count));
					// 取两个运算符之间的数字
				}
				peekOp = opStack.peek();
				if (currentOp == ')') {
					// 遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号
					while (opStack.peek() != '(') {
						postfixStack.push(String.valueOf(opStack.pop()));
					}
					opStack.pop();
				} else {
					while (currentOp != '(' && peekOp != ',' && compare(currentOp, peekOp)) {
						postfixStack.push(String.valueOf(opStack.pop()));
						peekOp = opStack.peek();
					}
					opStack.push(currentOp);
				}
				count = 0;
				currentIndex = i + 1;
			} else {
				count++;
			}
		}
		if (count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {
			// 最后一个字符不是括号或者其他运算符的则加入后缀式栈中
			postfixStack.push(new String(arr, currentIndex, count));
		}
		while (opStack.peek() != ',') {
			postfixStack.push(String.valueOf(opStack.pop()));
			// 将操作符栈中的剩余的元素添加到后缀式栈中
		}
	}
	/**
   * 判断是否为算术符号
   *
   * @param c
   * @return
   */
	private Boolean isOperator(char c) {
		return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')';
	}
	/**
   * 利用ASCII码-40做下标去算术符号优先级
   *
   * @param cur
   * @param peek
   * @return
   */
	public Boolean compare(char cur, char peek) {
		// 如果是peek优先级高于cur,返回true,默认都是peek优先级要低
		Boolean result = false;
		if (operatPriority[(peek) - 40] >= operatPriority[(cur) - 40]) {
			result = true;
		}
		return result;
	}
	/**
   * 按照给定的算术运算符做计算
   *
   * @param firstValue
   * @param secondValue
   * @param currentOp
   * @return
   */
	private String calculate(String firstValue, String secondValue, char currentOp) {
		String result = "";
		switch (currentOp) {
			case '+':
			      result = String.valueOf(ArithHelper.add(firstValue, secondValue));
			break;
			case '-':
			      result = String.valueOf(ArithHelper.sub(firstValue, secondValue));
			break;
			case '*':
			      result = String.valueOf(ArithHelper.mul(firstValue, secondValue));
			break;
			case '/':
			      result = String.valueOf(ArithHelper.div(firstValue, secondValue));
			break;
		}
		return result;
	}
}

ArithHelper 代码如下:

public class ArithHelper {
	// 默认除法运算精度
	private static final int DEF_DIV_SCALE = 16;
	// 这个类不能实例化
	private ArithHelper() {
	}
	/**
   * 提供精确的加法运算。
   *
   * @param v1 被加数
   * @param v2 加数
   * @return 两个参数的和
   */
	public static double add(double v1, double v2) {
		java.math.BigDecimal b1 = new java.math.BigDecimal(double.toString(v1));
		java.math.BigDecimal b2 = new java.math.BigDecimal(double.toString(v2));
		return b1.add(b2).doubleValue();
	}
	public static double add(String v1, String v2) {
		java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
		java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
		return b1.add(b2).doubleValue();
	}
	/**
   * 提供精确的减法运算。
   *
   * @param v1 被减数
   * @param v2 减数
   * @return 两个参数的差
   */
	public static double sub(double v1, double v2) {
		java.math.BigDecimal b1 = new java.math.BigDecimal(double.toString(v1));
		java.math.BigDecimal b2 = new java.math.BigDecimal(double.toString(v2));
		return b1.subtract(b2).doubleValue();
	}
	public static double sub(String v1, String v2) {
		java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
		java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
		return b1.subtract(b2).doubleValue();
	}
	/**
   * 提供精确的乘法运算。
   *
   * @param v1
   *      被乘数
   * @param v2
   *      乘数
   * @return 两个参数的积
   */
	public static double mul(double v1, double v2) {
		java.math.BigDecimal b1 = new java.math.BigDecimal(double.toString(v1));
		java.math.BigDecimal b2 = new java.math.BigDecimal(double.toString(v2));
		return b1.multiply(b2).doubleValue();
	}
	public static double mul(String v1, String v2) {
		java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
		java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
		return b1.multiply(b2).doubleValue();
	}
	/**
   * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。
   *
   * @param v1
   *      被除数
   * @param v2
   *      除数
   * @return 两个参数的商
   */
	public static double div(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE);
	}
	public static double div(String v1, String v2) {
		java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
		java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
		return b1.divide(b2, DEF_DIV_SCALE, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
	}
	/**
   * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。
   *
   * @param v1 被除数
   * @param v2 除数
   * @param scale 表示表示需要精确到小数点以后几位。
   * @return 两个参数的商
   */
	public static double div(double v1, double v2, int scale) {
		if (scale < 0) {
			throw new IllegalArgumentException("The  scale  must  be  a  positive  integer  or  zero");
		}
		java.math.BigDecimal b1 = new java.math.BigDecimal(double.toString(v1));
		java.math.BigDecimal b2 = new java.math.BigDecimal(double.toString(v2));
		return b1.divide(b2, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
	}
	/**
   * 提供精确的小数位四舍五入处理。
   *
   * @param v 需要四舍五入的数字
   * @param scale 小数点后保留几位
   * @return 四舍五入后的结果
   */
	public static double round(double v, int scale) {
		if (scale < 0) {
			throw new IllegalArgumentException("The  scale  must  be  a  positive  integer  or  zero");
		}
		java.math.BigDecimal b = new java.math.BigDecimal(double.toString(v));
		java.math.BigDecimal one = new java.math.BigDecimal("1");
		return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
	}
	public static double round(String v, int scale) {
		if (scale < 0) {
			throw new IllegalArgumentException("The  scale  must  be  a  positive  integer  or  zero");
		}
		java.math.BigDecimal b = new java.math.BigDecimal(v);
		java.math.BigDecimal one = new java.math.BigDecimal("1");
		return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
	}
}

使用时调用 Calculator 类的 conversion()方法,并传入算术表达式参数,即可返回一个 Double 类型的值。

使用示例:

public class MathTest {
	public static void main(String[] args) {
		String expression = "(0*1--3)-5/-4-(3*(-2.13))";
		double result = Calculator.conversion(expression);
		System.out.println(expression + " = " + result);
		System.out.println();
	}
}

控制台输出:

(0*1--3)-5/-4-(3*(-2.13)) = 10.64

测试截图:

总结

以上就是本文关于Java计算数学表达式代码详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

(0)

相关推荐

  • Java使用DateUtils对日期进行数学运算经典应用示例【附DateUtils相关包文件下载】

    本文实例讲述了Java使用DateUtils对日期进行数学运算的方法.分享给大家供大家参考,具体如下: 最近在写数据上传的程序,需要对Date进行一些数学运算,个人感觉在java中,日期的数学运算还是比较常用的,所以把Date的数学运算都玩了一下.试了一下,发现DateUtils这个工具类对于Date的数学运算非常方便,见代码吧. package date; import java.text.SimpleDateFormat; import java.util.Date; import org.

  • java中的数学计算函数的总结

    java中的数学计算函数 Math类: java.lang.Math类中包含基本的数字操作,如指数.对数.平方根和三角函数. java.math是一个包,提供用于执行任意精度整数(BigInteger)算法和任意精度小数(BigDecimal)算法的类. java.lang.Math类中包含E和PI两个静态常量,以及进行科学计算的类(static)方法,可以直接通过类名调用. public static final Double E = 2.7182818284590452354 public

  • java数学归纳法非递归求斐波那契数列的方法

    本文实例讲述了java数学归纳法非递归求斐波那契数列的方法.分享给大家供大家参考.具体如下: Integer能表示的最大值为 2147483647 大概是21.4亿,这里没有考虑溢出情况(当size为983时就会溢出)! import java.util.List; import java.util.ArrayList; /** * @author jxqlovejava * 斐波那契数列 */ public class Fibonacci { public static List<Intege

  • Java计算数学表达式代码详解

    Java字符串转换成算术表达式计算并输出结果,通过这个工具可以直接对字符串形式的算术表达式进行运算,并且使用非常简单. 这个工具中包含两个类 Calculator 和 ArithHelper Calculator 代码如下: import java.util.Collections; import java.util.Stack; /** * 算数表达式求值 * 直接调用Calculator的类方法conversion() * 传入算数表达式,将返回一个浮点值结果 * 如果计算过程错误,将返回一

  • Java数据溢出代码详解

    java是一门相对安全的语言,那么数据溢出时它是如何处理的呢? 看一段代码, public class Overflow { /** * @param args */ public static void main(String[] args) { int big = 0x7fffffff; //max int value System.out.println("big = " + big); int bigger = big * 4; System.out.println("

  • Java同步函数代码详解

    /* 同步函数 当函数中的代码全部放在了同步代码块中,那么这个函数就是同步函数 */ //同步函数的锁是this锁,this是一个引用,this指向的对象就是锁 //下面证明一下同步函数的锁就是this //创建两个线程,一个在同步代码块中执行,另一个在同步函数中执行 //同步代码块用的锁是obj,同步函数用的所是this //这就导致了两个线程存在两把锁,会出现上次所说的安全问题,即出现错误数据 //只有两个线程同时用一把锁,才能解决多线程的安全问题 class Ticket implemen

  • java多线程中断代码详解

    一.java中终止线程主要有三种方法: ①线程正常退出,即run()方法执行完毕了 ②使用Thread类中的stop()(已过期不推荐使用)方法强行终止线程. ③使用中断机制 t.stop()调用时,终止线程,会导致该线程所持有的锁被强制释放,从而被其他线程所持有,因此有可能导致与预期结果不一致.下面使用中断信号量中断非阻塞状态的线程中: public class TestStopThread { public static void main(String[] args) throws Int

  • Java多线程同步器代码详解

    同步器 为每种特定的同步问题提供了解决方案,同步器是一些使线程能够等待另一个线程的对象,允许它们协调动作.最常用的同步器是CountDownLatch和Semaphore,不常用的是Barrier 和Exchanger Semaphore Semaphore[信号标:旗语],通过计数器控制对共享资源的访问. 测试类: package concurrent; import concurrent.thread.SemaphoreThread; import java.util.concurrent.

  • java匿名内部类实例代码详解

    这篇文章主要介绍了java匿名内部类实例代码详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Person.java package insof; public class Person extends Object{ String name; static int age; public Person() { this.name = "tom"; System.out.println("执行的是构造方法");

  • java对象初始化代码详解

    本文主要记录JAVA中对象的初始化过程,包括实例变量的初始化和类变量的初始化以及final关键字对初始化的影响.另外,还讨论了由于继承原因,探讨了引用变量的编译时类型和运行时类型 一,实例变量的初始化 这里首先介绍下创建对象的过程: 类型为Dog的一个对象首次创建时,或者Dog类的static字段或static方法首次访问时,Java解释器必须找到Dog.class(在事先设定好的路径里面搜索):  找到Dog.class后(它会创建一个Class对象),它的所有static初始化模块都会运行.

  • Java线程死锁代码详解

    /** * @author hollis */ public class JStackDemo { public static void main(String[] args) { Thread t1 = new Thread(new DeadLockclass(true));//建立一个线程 Thread t2 = new Thread(new DeadLockclass(false));//建立另一个线程 t1.start();//启动一个线程 t2.start();//启动另一个线程 }

  • Java中的数组复制(clone与arraycopy)代码详解

    JAVA数组的复制是引用传递,而并不是其他语言的值传递. 1.clone protectedObjectclone() throwsCloneNotSupportedException创建并返回此对象的一个副本."副本"的准确含义可能依赖于对象的类.这样做的目的是,对于任何对象x,表达式: x.clone()!=x为true,表达式: x.clone().getClass()==x.getClass()也为true,但这些并非必须要满足的要求.一般情况下: x.clone().equa

  • java中switch选择语句代码详解

    switch结构(开关语句)的语法 switch(表达式 ){ --->类型为int.char case 常量1 :--->case 结构可以有多个 //语句块1 break; --->程序跳出switch结构 case 常量n :--->常量的值不能相同 //语句块n break; default:--->和if结构中的 else作用相同 //语句块 break; } 下面看一段代码示例,有详细的注释,大家可以参考: public class SwitchStu{ /* s

随机推荐