计算入口(calExp方法)
import java.util.Collections;
import java.util.Stack;
/**
* 算数表达式求值
* 直接调用Calculator的类方法conversion()
* 传入算数表达式,将返回一个浮点值结果
* 如果计算过程错误,将返回一个NaN
*/
public class Calculator {
private Stack postfixStack = new Stack();// 后缀式栈
private Stack opStack = new Stack();// 运算符栈
private int[] operatPriority = new int[] { 0, 3, 2, 1, -1, 1, 0, 2 };// 运用运算符ASCII码-40做索引的运算符优先级
/**
* 计算字符串式子(计算表达式)
*
Description :
* @param expression String
* @return double
*
Date: 2020/1/4 13:31
Author : dxl
*/
public static double calExp(String expression) {
double result = 0;
Calculator cal = new Calculator();
try {
//DXL先进行特殊函数运算
expression = FunHelper.subCalculate(expression);
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) {
//DXL
//好像是处理最前面负号(最开始做的,忘了)
/////////////////////////////////////////////////////////////////////////////////////////////////////////
for (int i = 0; i < expression.length(); i++){
if (expression.charAt(i) == '-'){
if (i == 0){
expression = "0" + expression;
}else{
char c = expression.charAt(i-1);
if (c == '(' ){
expression = expression.substring(0, i) + "0" + expression.substring(i, expression.length());
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
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]=='('){
if(arr[0]=='~'){
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 resultStack = new Stack();
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);
}
}
// System.out.println("double��calculate�е�resultStack��"+Double.valueOf(resultStack.pop()));
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;
}
/////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
public static void main(String[] args) {
// String expression = "2+3+sin(30+20-20+1-sin(45+45))";
// String expression = "2+++++3";
// String expression = "sin(0.0/0.0)";
// String expression = "arcsin((20)/2/16)";
String expression = "2+3+sin(13-tan(90/2)+sin(44+cos(50-20-30)+60-30/2))+tan(45)+2e1*arcsin(1/2.5)";
// String expression = "(((sin(20+-10))))";
// String expression = "5+(((-(((2+2)))/2)))";
// String expression = "5+(-(((2+2)))/2)";
// String expression = "5+(-(((2+2)))/2)";
// String expression = "5+(-(2+2)/2)";
double result = Calculator.calExp(expression);
System.out.println(expression + " = " + result);
System.out.println();
}
}
使用java.math.BigDecimal计算double,保证double的计算精度
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();
}
}
做一些计算前处理,如转换、优先级比较
import java.util.ArrayList;
import java.util.Stack;
public class CalHelper {
/**
* 将字符串转化成List
* @param str
* @return
*/
public ArrayList getStringList(String str){
ArrayList result = new ArrayList();
String num = "";
for (int i = 0; i < str.length(); i++) {
if(Character.isDigit(str.charAt(i))){
num = num + str.charAt(i);
}else{
if(num != ""){
result.add(num);
}
result.add(str.charAt(i) + "");
num = "";
}
}
if(num != ""){
result.add(num);
}
return result;
}
/**
* 将中缀表达式转化为后缀表达式
* @param inOrderList
* @return
*/
//result = [12, +, (, 23, *, 3, -, 56, +, 7, ), *, (, 2, +, 90, ), /, 2]
public ArrayList getPostOrder(ArrayList inOrderList){
ArrayList result = new ArrayList();
Stack stack = new Stack();
for (int i = 0; i < inOrderList.size(); i++) {
if(Character.isDigit(inOrderList.get(i).charAt(0))){
result.add(inOrderList.get(i));
}else{
switch (inOrderList.get(i).charAt(0)) {
case '(':
stack.push(inOrderList.get(i));
break;
case ')':
while (!stack.peek().equals("(")) {
result.add(stack.pop());
}
stack.pop();
break;
default:
while (!stack.isEmpty() && compare(stack.peek(), inOrderList.get(i))){
result.add(stack.pop());
}
stack.push(inOrderList.get(i));
break;
}
}
}
while(!stack.isEmpty()){
result.add(stack.pop());
}
return result;
}
/**
* 计算后缀表达式
* @param postOrder
* @return
*/
//[12, 23, 3, *, 56, -, 7, +, 2, 90, +, *, 2, /, +]result
@SuppressWarnings({ "unchecked", "rawtypes" })
public Double calculate(ArrayList postOrder){
Stack stack = new Stack();
for (int i = 0; i < postOrder.size(); i++) {
if(Character.isDigit(postOrder.get(i).charAt(0))){
stack.push(Double.parseDouble(postOrder.get(i)));
}else{
Double back = (Double)stack.pop();
Double front = (Double)stack.pop();
Double res = 0.0;
switch (postOrder.get(i).charAt(0)) {
case '+':
res = front + back;
break;
case '-':
res = front - back;
break;
case '*':
res = front * back;
break;
case '/':
res = front / back;
break;
}
stack.push(res);
}
}
return (Double)stack.pop();
}
/**
* 比较运算符等级
* @param peek
* @param cur
* @return
*/
public static boolean compare(String peek, String cur){
if("*".equals(peek) && ("/".equals(cur) || "*".equals(cur) ||"+".equals(cur) ||"-".equals(cur))){
return true;
}else if("/".equals(peek) && ("/".equals(cur) || "*".equals(cur) ||"+".equals(cur) ||"-".equals(cur))){
return true;
}else if("+".equals(peek) && ("+".equals(cur) || "-".equals(cur))){
return true;
}else if("-".equals(peek) && ("+".equals(cur) || "-".equals(cur))){
return true;
}
return false;
}
}
处理表达式中数学函数的计算,根据需求可在(funCalculate)这里面添加
public class FunHelper {
////////////////////////////////////////
/**
* 应该是支持函数运算(以前做的,注释忘了)
* @param funname
* @param substring
* @return String substring
*/
public static String funCalculate (String funname,String substring){
double subvalue = Calculator.calExp(substring);
if (Double.isNaN(subvalue)){
System.out.println("计算"+substring+"出错 :(");
substring = "+++";
return substring;
}
switch (funname) {
case "arcsin":
subvalue = Math.asin(subvalue)*180/Math.PI;
substring = String.valueOf(subvalue);
break;
case "arccos":
subvalue = Math.acos(subvalue)*180/Math.PI;
substring = String.valueOf(subvalue);
break;
case "arctan":
subvalue = Math.atan(subvalue)*180/Math.PI;
break;
case "sin":
subvalue = Math.sin(Math.PI*subvalue/180);
substring = String.valueOf(subvalue);
break;
case "cos":
subvalue = Math.cos(Math.PI*subvalue/180);
substring = String.valueOf(subvalue);
break;
case "tan":
subvalue = Math.tan(Math.PI*subvalue/180);
substring = String.valueOf(subvalue);
break;
default : System.out.println("不支持"+funname+"() :(");
substring = "+++";
break;
}
return substring;
}
///////////////////////////////////////////
public static String subCalculate(String expression){
for (int i = 0; i < expression.length(); i++) {
char temchar1 = expression.charAt(i);
Integer tem1 = Integer.valueOf(temchar1);
if(tem1 >96 && tem1 < 123 && tem1 != 101){
int indexleft = i;
int indexright = i;
int p = 0;
for (int j = i;j < expression.length(); j++){
if (expression.charAt(j) == '(' ){
indexleft = j;
break;
}
}
for (int t = indexleft+1;t < expression.length(); t++){
if (expression.charAt(t) == '('){
p++;
}else{
if (expression.charAt(t) == ')'){
p--;
}
}
if (p == -1){
indexright = t;
break;
}
}
String substring = expression.substring(indexleft+1, indexright);
String subvalue = FunHelper.funCalculate(expression.substring(i, indexleft),substring);
String funstring = expression.substring(i, indexright+1);
expression = expression.replace(funstring,subvalue);
}
}
return expression;
}
}
PS:很早指点优化的,现在重新发布下,自行修改