包含简单科学运算的表达式求值

包含简单科学运算的表达式求值


想写个计算器,但又没有在网上找到直接可用的开源代码,于是自己试着捣鼓一下,写了这个工具类。

源代码

package mc.math;

import java.math.BigDecimal;
import java.util.Stack;

/**
 * @author 明明
 * <p>
 * 这个类用来计算算术表达式,返回计算的结果
 * <p>
 * 支持以下运算符号:
 * <p>
 * +(加), -(减), *(乘), /(除), ((左括号), )(右括号), ^(幂), ²(平方), ³(立方), 
 * <p>
 * sin(正弦), cos(余弦), tan(正切), ln(e为底的对数), log(10为底的对数), !(阶乘), √(根号)
 * <p>
 * 支持以下常量:
 * <p>
 * π(圆周率), e(自然常数)
 */
public class Calculator {
    // s表示sin,c表示cos, t表示tan, l表示ln, o表示log, ~表示负号
    private static final char ADD = '+';
    private static final char SUBTRACT = '-';
    private static final char MINUS = '~'; // 负号
    private static final char MULTIPLY = '*';
    private static final char DIVIDE = '/';
    private static final char LEFT_PARENTHESIS = '(';
    private static final char RIGHT_PARENTHESIS = ')';
    private static final char POW = '^';
    private static final char POW2 = '²';
    private static final char POW3 = '³';
    private static final char SIN = 's';
    private static final char COS = 'c';
    private static final char TAN = 't';
    private static final char LN = 'l';
    private static final char LOG = 'o';
    private static final char FACTORIAL = '!';
    private static final char SQRT = '√';
//  private static final char PI = 'π';
//  private static final char E = 'e';
    private static final char COMMA = ',';

    private Stack<Character> operatorStack;
    private Stack<String> operandStack;
    private static Calculator calculator = null;

    private Calculator() {
        operatorStack = new Stack<Character>();
        operandStack = new Stack<String>();
    }

    private static void initCalculator() {
        // Use singleton pattern(单例模式)
        if (calculator == null) {
            synchronized (Calculator.class) {
                if (calculator == null)
                    calculator = new Calculator();
            }
        }
        calculator.operatorStack.clear();
        calculator.operatorStack.push(COMMA);
        calculator.operandStack.clear();
    }
    /**
     * 计算算术表达式并返回计算结果
     * @param expression 算术表达式
     * @return 算术表达式的结果
     */
    public static String calculateExpression(String expression) {
        initCalculator();
        expression = expression.replaceAll("sin", "s")
            .replaceAll("cos", "c")
            .replaceAll("tan", "t")
            .replaceAll("ln", "l")
            .replaceAll("log", "o")
            .replaceAll("π", String.valueOf(Math.PI))
            .replaceAll("e", String.valueOf(Math.E));
        int currentIndex = 0;
        int count = 0;
        char peekOperator;
        while (currentIndex < expression.length()) {
            char currentChar = expression.charAt(currentIndex);
            // 右结合单目运算符直接入符号栈
            if (isUnaryOperator(currentChar)) {
                calculator.operatorStack.push(currentChar);
                currentIndex++;
            // 左结合单目运算符直接运算
            } else if (currentChar == FACTORIAL || currentChar == POW2 ||
                    currentChar == POW3) {
                String result = calculate(calculator.operandStack.pop(), currentChar);
                calculator.operandStack.push(result);
                currentIndex++;
            } else if (isBinaryOperator(currentChar)) { // 双目运算符
                // 检查是否是负号,负号是右结合单目运算符,直接入符号栈
                if (currentChar == SUBTRACT) {
                    if (currentIndex == 0 || isOperator(
                            expression.charAt(currentIndex - 1))) {
                        calculator.operatorStack.push(MINUS);
                        currentIndex++;
                        continue;
                    }
                }
                peekOperator = calculator.operatorStack.peek();
                while (peekOperator != COMMA) {
                    // 如果前一个运算符是右结合单目运算符
                    if (isUnaryOperator(peekOperator)) {
                        String result = calculate(calculator.operandStack.pop(),
                                calculator.operatorStack.pop());
                        calculator.operandStack.push(result);
                    // 如果前一个运算符是双目运算符,并且其优先级大于等于当前运算符的优先级
                    } else if (compareOperatorPriority(currentChar, peekOperator) <= 0) {
                        binaryCalculate();
                    } else {
                        break;
                    }
                    peekOperator = calculator.operatorStack.peek();
                }
                calculator.operatorStack.push(currentChar);
                currentIndex++;
            } else if (currentChar == LEFT_PARENTHESIS) { // 左括号"("直接入符号栈
                calculator.operatorStack.push(currentChar);
                currentIndex++;
            } else if (currentChar == RIGHT_PARENTHESIS) { // 右括号 )
                peekOperator = calculator.operatorStack.peek();
                while (peekOperator != LEFT_PARENTHESIS) {
                    // 如果前一个运算符是右结合单目运算符
                    if (isUnaryOperator(peekOperator)) {
                        String result = calculate(calculator.operandStack.pop(),
                                calculator.operatorStack.pop());
                        calculator.operandStack.push(result);
                    // 如果前一个运算符是双目运算符
                    } else {
                        binaryCalculate();
                    }
                    peekOperator = calculator.operatorStack.peek();
                }
                calculator.operatorStack.pop();
                currentIndex++;
            // 如果是数字
            } else {
                do {
                    count++;
                    if (currentIndex + count >= expression.length()) break;
                } while (isDigitOrPoint(expression.charAt(currentIndex + count)));
                calculator.operandStack.push(
                        expression.substring(currentIndex, currentIndex + count));
                currentIndex += count;
                count = 0;
            }
        }

        peekOperator = calculator.operatorStack.peek();
        while (peekOperator != COMMA) {
            // 如果前一个运算符是右结合单目运算符
            if (isUnaryOperator(peekOperator)) {
                String result = calculate(calculator.operandStack.pop(),
                        calculator.operatorStack.pop());
                calculator.operandStack.push(result);
            // 如果前一个运算符是双目运算符
            } else {
                binaryCalculate();
            }
            peekOperator = calculator.operatorStack.peek();
        }

        return removerRedundantZero(calculator.operandStack.pop());
    }
    // 去除小数点后多余的0
    private static String removerRedundantZero(String s) {
        if (s.contains(".")) {
            int startIndex = s.indexOf('.') - 1;
            for (int i = startIndex + 2, sLength = s.length(); i < sLength; i++) {
                if (s.charAt(i) != '0') {
                    startIndex = i;
                }
            }
            return s.substring(0, startIndex + 1);
        }
        return s;
    }
    // 双目运算
    private static void binaryCalculate() {
        String n2 = calculator.operandStack.pop();
        String n1 = calculator.operandStack.pop();
        String result = calculate(n1, n2, calculator.operatorStack.pop());
        calculator.operandStack.push(result);
    }


    private static boolean isOperator(char c) {
        // s表示sin,c表示cos, t表示tan, l表示ln, o表示log, ~表示负号
        return (c == ADD ||
                c == SUBTRACT ||
                c == MULTIPLY ||
                c == DIVIDE ||
                c == POW ||
                c == SIN ||
                c == COS ||
                c == TAN ||
                c == LN ||
                c == LOG ||
                c == SQRT);
        // π
    }
    // 判断是否是右结合单目运算符,是则返回true
    private static boolean isUnaryOperator(char c) {
        return (c == MINUS ||
                c == SIN ||
                c == COS ||
                c == TAN ||
                c == LN ||
                c == LOG ||
                c == SQRT);
    }
    // 判断是否是双目运算符,是则返回true
    private static boolean isBinaryOperator(char c) {
        return (c == ADD ||
                c == SUBTRACT ||
                c == MULTIPLY ||
                c == DIVIDE ||
                c == POW);
    }
    // 判断是否是数字或点(小数点),是则返回true
    private static boolean isDigitOrPoint(char c) {
        return (Character.isDigit(c) || c == '.');
    }

    /* 比较两个运算符的优先级,
     * operator1等于operator2则返回0
     * operator1大于operator2则返回大于0的数,
     * operator1小于operator2则返回小于0的数
     */
    private static int compareOperatorPriority(char operator1, char operator2) {
                               // ,  (  +  -  *  /  ^  )
        int[] operatorPriority = {0, 1, 2, 2, 3, 3, 4, 5};
        int index1 = 0, index2 = 0;

        switch (operator1) {
            case COMMA:
                index1 = 0; break;
            case LEFT_PARENTHESIS:
                index1 = 1; break;
            case ADD:
                index1 = 2; break;
            case SUBTRACT:
                index1 = 3; break;
            case MULTIPLY:
                index1 = 4; break;
            case DIVIDE:
                index1 = 5; break;
            case POW:
                index1 = 6; break;
            case RIGHT_PARENTHESIS:
                index1 = 7; break;
        }

        switch (operator2) {
            case COMMA:
                index2 = 0; break;
            case LEFT_PARENTHESIS:
                index2 = 1; break;
            case ADD:
                index2 = 2; break;
            case SUBTRACT:
                index2 = 3; break;
            case MULTIPLY:
                index2 = 4; break;
            case DIVIDE:
                index2 = 5; break;
            case POW:
                index2 = 6; break;
            case RIGHT_PARENTHESIS:
                index2 = 7; break;
        }

        return operatorPriority[index1] - operatorPriority[index2];
    }
    // 双目运算
    private static String calculate(String n1, String n2, char operator) {
        BigDecimal bD1 = new BigDecimal(n1);
        BigDecimal bD2 = new BigDecimal(n2);
        switch (operator) {
            case ADD: return bD1.add(bD2).toString();
            case SUBTRACT: return bD1.subtract(bD2).toString();
            case MULTIPLY: return bD1.multiply(bD2).toString();
            case DIVIDE: 
                try {
                    return bD1.divide(bD2).toString();
                } catch (Exception ex) {
                    return bD1.divide(bD2, 100, BigDecimal.ROUND_HALF_UP).toString();
                }
            default: return "0";
        }
    }
    // 单目运算
    private static String calculate(String n, char operator) {
        BigDecimal bd = new BigDecimal(n);
        switch (operator) {
            case FACTORIAL:
                BigDecimal tempResult = BigDecimal.ONE;
                for (int i = 2, length = bd.intValue(); i <= length; i++) {
                    tempResult = tempResult.multiply(new BigDecimal(String.valueOf(i)));
                }
                return tempResult.toString();
            case POW2: return bd.multiply(bd).toString();
            case POW3: return bd.multiply(bd).multiply(bd).toString();
            case MINUS: return bd.multiply(new BigDecimal("-1")).toString();
            case SIN: return String.valueOf(Math.sin(bd.doubleValue()));
            case COS: return String.valueOf(Math.cos(bd.doubleValue()));
            case TAN: return String.valueOf(Math.tan(bd.doubleValue()));
            case SQRT: return String.valueOf(Math.sqrt(bd.doubleValue()));
            case LN: return String.valueOf(Math.log(bd.doubleValue()));
            case LOG: return String.valueOf(Math.log10(bd.doubleValue()));
            default: return "0";
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值