一、运算原理
计算器类有Calculator类、BaseCalculator类、ScienceCalculator类。Calculator类用来表示计算器的状态。BaseCalculator类用来运算基本数学表达式(+ - X / 操作,其中还有E运算,用来运算2E(-16)这种小数的运算,或者3E(15)这种超大数运算 ),ScienceCalculator类用来完成科学运算,进行如sin、cos、tan等的运算,并将科学数学表达式转换为基本运算器BaseCalculator类可以完成的基本数学表达式
二、calculator类
1.作用
此类用来表示当前计算器的状态,如是否有新的运算式,是否为科学模式,是否为弧度制
2.原理
(1)中缀表达式转后缀表达式
- 初始化两个栈:运算符栈s1和储存后缀表达式的队列s2; 从左至右扫描中缀表达式; 遇到操作数时,将其压入s2;
- 遇到运算符时,比较其与s1栈顶运算符的优先级: 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
- 否则,若优先级比栈顶运算符的高,也将运算符压入s1(注意转换为前缀表达式时是优先级较高或相同,而这里则不包括相同的情况);
- 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较; 遇到括号时:
- 如果是左括号“(”,则直接压入s1;
- 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃;
- 重复步骤2至5,直到表达式的最右边; 将s1中剩余的运算符依次弹出并压入s2;
- 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式(转换为前缀表达式时不用逆序)
(2)运算后缀表达式
- 从左到右扫描后缀表达式,如果是操作数,就压入操作数栈s3,如果是操作符,就连续弹出两个操作数(注意:后弹出的为第一操作数,先弹出的为第二操作数),然后进行操作符的操作,将新生成的操作数压入栈中,反复直到后缀表达式扫面完毕,栈s3中只存在一个数,就是最终结果
2.代码
public class Calculator {
//是否为科学模式
private boolean isScientific=false;
//是否为新运算式
private boolean isNew=true;
//是否为弧度制
private boolean isRad=true;
public boolean isScientific() {
return isScientific;
}
public void setScientific(boolean scientific) {
isScientific = scientific;
}
public boolean isNew() {
return isNew;
}
public void setNew(boolean aNew) {
isNew = aNew;
}
public boolean isRad() {
return isRad;
}
public void setRad(boolean rad) {
isRad = rad;
}
}
二、BaseCalculator类
1.作用
此类用于实现+ - x / E运算
2.代码实现
package com.example.calculator.model;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Stack;
public class BaseCalculator {
//用来存取数和运算符
private class Ele {
public boolean isNum;
public double o;
public char op;
public Ele(double o,boolean isNum){
this.isNum=isNum;
this.o=o;
}
public Ele(char op,boolean isNum) {
this.isNum = isNum;
this.op = op;
}
}
//Map结构方便后面取运算符的优先级
private final Map<Character, Integer> operMap = new HashMap<Character, Integer>() {{
put('+', 0);
put('-', 0);
put('×', 1);
put('/', 1);
put('E', 2);
put('(', 3);
put(')', 3);
}};
//简单四则运算
private double operate(double a, char oper, double b) {
switch (oper) {
case '+':
return a + b;
case '-':
return a - b;
case '×':
return a * b;
case '/':
if (b == 0) {
return Double.MAX_VALUE; //处理异常
}
return a / b;
case 'E':
return Double.parseDouble(String.valueOf(a+"E"+(int)b));
default:
return 0;
}
}
//计算
public double cal(String math) {
if (math.length() == 0) {
return Double.MAX_VALUE;
} else {
Stack<Character> operStack = new Stack<>(); //oper栈
Queue<Ele> eleQueue = new LinkedList<>();
Stack<Double> numStack = new Stack<>();
//中缀变后缀
int firstIndex = 0;
int i=0;
while (i < math.length()) {
char charOfMath = math.charAt(i);
if (charOfMath == '(') {
operStack.push(charOfMath);
i++;
} else if (charOfMath == ')') {
while (operStack.peek() != '(') {
eleQueue.offer(new Ele(operStack.pop(), false));
}
operStack.pop();
i++;
}else if(isNum(charOfMath)){
firstIndex=i;
while (++i<math.length()&&isNum(math.charAt(i)));
double num;
try {
num = Double.parseDouble(math.substring(firstIndex, i));
} catch (NumberFormatException e) {
return Double.MAX_VALUE;
}
eleQueue.offer(new Ele(num,true));
}else{
//说明-是单目运算符
if (charOfMath == '-') {
if (i == 0) {
firstIndex=0;
while (++i<math.length()&&isNum(math.charAt(i)));
double num;
try {
num = Double.parseDouble(math.substring(firstIndex, i));
} catch (NumberFormatException e) {
return Double.MAX_VALUE;
}
eleQueue.offer(new Ele(num,true));
continue;
} else if (math.charAt(i - 1)=='(') {
//表示这是一个单目运算符
firstIndex=i;
while (++i<math.length()&&isNum(math.charAt(i)));
double num;
try {
num = Double.parseDouble(math.substring(firstIndex, i));
} catch (NumberFormatException e) {
return Double.MAX_VALUE;
}
eleQueue.offer(new Ele(num,true));
continue;
}
}
while (operStack.size() >= 0) {
if (operStack.size() == 0 || operStack.peek() == '(') {
operStack.push(charOfMath);
break;
} else {
if (operMap.get(operStack.peek()) < operMap.get(charOfMath)) {
operStack.push(charOfMath);
break;
} else {
eleQueue.offer(new Ele(operStack.pop(), false));
}
}
}
i++;
}
}
//将剩余操作符压入后缀表达式
while (operStack.size()>0){
eleQueue.offer(new Ele(operStack.pop(),false));
}
//检查后缀并运算
while (eleQueue.size() > 0) {
if (eleQueue.element().isNum) {
numStack.push(eleQueue.poll().o);
} else {
char op = eleQueue.poll().op;
double second= numStack.pop();
double first = numStack.pop();
numStack.push(operate(first, op, second));
}
}
return numStack.peek();
}
}
private boolean isNum(char c){
return (c-'0'>=0&&c-'0'<=9)||c=='.';
}
}