需要根據配置的表達式(例如:5+12*(3+5)/7.0)計算出相應的結果,因此使用java中的棧利用后綴表達式的方式實現該工具類。
后綴表達式就是將操作符放在操作數的后面展示的方式,例如:3+2 后綴表達式為32+,3*(2+1)的后綴表達式為:321+*,解決表達式求值首先需要根據字符串表達式求出后綴表達式,然后使用后綴表達式和操作數棧實現計算,計算的大致思想是從后綴表達式中取元素,如果元素是數值則加入到操作數棧中,如果是運算符則從操作數棧中取兩個數來參與運算。后綴表達式的獲取要借助於兩個棧,一個是后綴表達式棧,一個是操作符棧,順序掃描算術表達式,如果是數值則直接加入后綴表達式棧,如果是運算符則使用當前運算符和運算符棧中的棧頂元素做比較,如果當前運算符的優先級高則當前元素進入操作符棧,如果當前元素優先級低,則操作符棧頂元素出棧加入到后綴表達式棧中,一直到當前元素優先級高於操作符棧頂元素優先級則當前元素入操作符棧,目前只支持加減乘除和帶小括號的運算。
1 import java.util.Collections; 2 import java.util.Stack; 3 4 public class Calculator { 5 private Stack<String> postfixStack = new Stack<String>();//后綴式棧 6 private Stack<Character> opStack = new Stack<Character>();//運算符棧 7 private int [] operatPriority = new int[] {0,3,2,1,-1,1,0,2};//運用運算符ASCII碼-40做索引的運算符優先級 8 public static void main(String[] args) { 9 System.out.println(5+12*(3+5)/7.0); 10 Calculator cal = new Calculator(); 11 String s = "5+12*(3+5)/7"; 12 double result = cal.calculate(s); 13 System.out.println(result); 14 } 15 16 /** 17 * 按照給定的表達式計算 18 * @param expression 要計算的表達式例如:5+12*(3+5)/7 19 * @return 20 */ 21 public double calculate(String expression) { 22 Stack<String> resultStack = new Stack<String>(); 23 prepare(expression); 24 Collections.reverse(postfixStack);//將后綴式棧反轉 25 String firstValue ,secondValue,currentValue;//參與計算的第一個值,第二個值和算術運算符 26 while(!postfixStack.isEmpty()) { 27 currentValue = postfixStack.pop(); 28 if(!isOperator(currentValue.charAt(0))) {//如果不是運算符則存入操作數棧中 29 resultStack.push(currentValue); 30 } else {//如果是運算符則從操作數棧中取兩個值和該數值一起參與運算 31 secondValue = resultStack.pop(); 32 firstValue = resultStack.pop(); 33 String tempResult = calculate(firstValue, secondValue, currentValue.charAt(0)); 34 resultStack.push(tempResult); 35 } 36 } 37 return Double.valueOf(resultStack.pop()); 38 } 39 40 /** 41 * 數據准備階段將表達式轉換成為后綴式棧 42 * @param expression 43 */ 44 private void prepare(String expression) { 45 opStack.push(',');//運算符放入棧底元素逗號,此符號優先級最低 46 char[] arr = expression.toCharArray(); 47 int currentIndex = 0;//當前字符的位置 48 int count = 0;//上次算術運算符到本次算術運算符的字符的長度便於或者之間的數值 49 char currentOp ,peekOp;//當前操作符和棧頂操作符 50 for(int i=0;i<arr.length;i++) { 51 currentOp = arr[i]; 52 if(isOperator(currentOp)) {//如果當前字符是運算符 53 if(count > 0) { 54 postfixStack.push(new String(arr,currentIndex,count));//取兩個運算符之間的數字 55 } 56 peekOp = opStack.peek(); 57 if(currentOp == ')') {//遇到反括號則將運算符棧中的元素移除到后綴式棧中直到遇到左括號 58 while(opStack.peek() != '(') { 59 postfixStack.push(String.valueOf(opStack.pop())); 60 } 61 opStack.pop(); 62 } else { 63 while(currentOp != '(' && peekOp != ',' && compare(currentOp,peekOp) ) { 64 postfixStack.push(String.valueOf(opStack.pop())); 65 peekOp = opStack.peek(); 66 } 67 opStack.push(currentOp); 68 } 69 count = 0; 70 currentIndex = i+1; 71 } else { 72 count++; 73 } 74 } 75 if(count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {//最后一個字符不是括號或者其他運算符的則加入后綴式棧中 76 postfixStack.push(new String(arr,currentIndex,count)); 77 } 78 79 while(opStack.peek() != ',') { 80 postfixStack.push(String.valueOf( opStack.pop()));//將操作符棧中的剩余的元素添加到后綴式棧中 81 } 82 } 83 84 /** 85 * 判斷是否為算術符號 86 * @param c 87 * @return 88 */ 89 private boolean isOperator(char c) { 90 return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' ||c == ')'; 91 } 92 93 /** 94 * 利用ASCII碼-40做下標去算術符號優先級 95 * @param cur 96 * @param peek 97 * @return 98 */ 99 public boolean compare(char cur,char peek) {// 如果是peek優先級高於cur,返回true,默認都是peek優先級要低 100 boolean result = false; 101 if(operatPriority[(peek)-40] >= operatPriority[(cur) - 40]) { 102 result = true; 103 } 104 return result; 105 } 106 107 /** 108 * 按照給定的算術運算符做計算 109 * @param firstValue 110 * @param secondValue 111 * @param currentOp 112 * @return 113 */ 114 private String calculate(String firstValue,String secondValue,char currentOp) { 115 String result = ""; 116 switch(currentOp) { 117 case '+': 118 result = String.valueOf(ArithHelper.add(firstValue, secondValue)); 119 break; 120 case '-': 121 result = String.valueOf(ArithHelper.sub(firstValue, secondValue)); 122 break; 123 case '*': 124 result = String.valueOf(ArithHelper.mul(firstValue, secondValue)); 125 break; 126 case '/': 127 result = String.valueOf(ArithHelper.div(firstValue, secondValue)); 128 break; 129 } 130 return result; 131 } 132 }
1 public class ArithHelper { 2 3 // 默認除法運算精度 4 private static final int DEF_DIV_SCALE = 16; 5 6 // 這個類不能實例化 7 private ArithHelper() { 8 } 9 10 /** 11 * 提供精確的加法運算。 12 * 13 * @param v1 被加數 14 * @param v2 加數 15 * @return 兩個參數的和 16 */ 17 18 public static double add(double v1, double v2) { 19 java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1)); 20 java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2)); 21 return b1.add(b2).doubleValue(); 22 } 23 24 public static double add(String v1, String v2) { 25 java.math.BigDecimal b1 = new java.math.BigDecimal(v1); 26 java.math.BigDecimal b2 = new java.math.BigDecimal(v2); 27 return b1.add(b2).doubleValue(); 28 } 29 30 /** 31 * 提供精確的減法運算。 32 * 33 * @param v1 被減數 34 * @param v2 減數 35 * @return 兩個參數的差 36 */ 37 38 public static double sub(double v1, double v2) { 39 java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1)); 40 java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2)); 41 return b1.subtract(b2).doubleValue(); 42 } 43 44 public static double sub(String v1, String v2) { 45 java.math.BigDecimal b1 = new java.math.BigDecimal(v1); 46 java.math.BigDecimal b2 = new java.math.BigDecimal(v2); 47 return b1.subtract(b2).doubleValue(); 48 } 49 50 /** 51 * 提供精確的乘法運算。 52 * 53 * @param v1 54 * 被乘數 55 * @param v2 56 * 乘數 57 * @return 兩個參數的積 58 */ 59 60 public static double mul(double v1, double v2) { 61 java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1)); 62 java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2)); 63 return b1.multiply(b2).doubleValue(); 64 } 65 66 public static double mul(String v1, String v2) { 67 java.math.BigDecimal b1 = new java.math.BigDecimal(v1); 68 java.math.BigDecimal b2 = new java.math.BigDecimal(v2); 69 return b1.multiply(b2).doubleValue(); 70 } 71 72 /** 73 * 提供(相對)精確的除法運算,當發生除不盡的情況時,精確到 小數點以后10位,以后的數字四舍五入。 74 * 75 * @param v1 76 * 被除數 77 * @param v2 78 * 除數 79 * @return 兩個參數的商 80 */ 81 82 public static double div(double v1, double v2) { 83 return div(v1, v2, DEF_DIV_SCALE); 84 } 85 86 public static double div(String v1, String v2) { 87 java.math.BigDecimal b1 = new java.math.BigDecimal(v1); 88 java.math.BigDecimal b2 = new java.math.BigDecimal(v2); 89 return b1.divide(b2, DEF_DIV_SCALE, java.math.BigDecimal.ROUND_HALF_UP).doubleValue(); 90 } 91 92 /** 93 * 提供(相對)精確的除法運算。當發生除不盡的情況時,由scale參數指 定精度,以后的數字四舍五入。 94 * 95 * @param v1 被除數 96 * @param v2 除數 97 * @param scale 表示表示需要精確到小數點以后幾位。 98 * @return 兩個參數的商 99 */ 100 101 public static double div(double v1, double v2, int scale) { 102 if (scale < 0) { 103 throw new IllegalArgumentException("The scale must be a positive integer or zero"); 104 } 105 java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1)); 106 java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2)); 107 return b1.divide(b2, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue(); 108 } 109 110 /** 111 * 提供精確的小數位四舍五入處理。 112 * 113 * @param v 需要四舍五入的數字 114 * @param scale 小數點后保留幾位 115 * @return 四舍五入后的結果 116 */ 117 118 public static double round(double v, int scale) { 119 if (scale < 0) { 120 throw new IllegalArgumentException("The scale must be a positive integer or zero"); 121 } 122 java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v)); 123 java.math.BigDecimal one = new java.math.BigDecimal("1"); 124 return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue(); 125 } 126 127 public static double round(String v, int scale) { 128 if (scale < 0) { 129 throw new IllegalArgumentException("The scale must be a positive integer or zero"); 130 } 131 java.math.BigDecimal b = new java.math.BigDecimal(v); 132 java.math.BigDecimal one = new java.math.BigDecimal("1"); 133 return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue(); 134 } 135 }