字符串公式计算方法

本文介绍了一种在Java中解析并计算字符串公式的方法,利用递归和双链表结构处理表达式,支持多种运算符,适用于精度要求高的场景,包括示例和注意事项。
摘要由CSDN通过智能技术生成

说明

其实这个应用场景并不多见,但碍于方法以及代码优秀,网络上也没有出现比较完美的解决方案,于是乎再一次从json-script-rule中扒下来一段代码,分享给大家,有需要的尽管拿去用吧!
提示1:老规矩,先认真读注释说明,看使用的例子,该方法不会造成精度问题,且性能测试良好,采用双链表结构加递归处理巧妙解决问题
提示2:java8以上的版本存在一些新的语法如getFirst(),如果你的版本是8那么可以大胆的替换成get(0)
提示3:calculate有多个重载方法,注意方法中调用的是递归calculate还是下面抽离出去的calculate

/**
     * <p>计算字符串中的公式,公式必须是合理的公式,不能有除运算符外的其它字符包括空格
     * <p>例子:calculate("-3.3*((-1.2+0.2)*(10%(-3.2/10)))"),结果:0.2640(默认四舍五入),程序计算结果:0.2639999999999993
     * @param expression 计算公式的表达式
     * @param scale 用于除法计算时保留的小数位
     * @param mode 选择舍入模式,默认四舍五入
     * */
    public static BigDecimal calculate(String expression,int scale, RoundingMode mode){
        try{
            if (isBlank(expression)){
                throw new JSRuleException();
            }
            while(expression.contains(ZSSign.RIGHT_BRACKET_STR)){
                StringBuilder equation = new StringBuilder(expression);
                int end = equation.indexOf(ZSSign.RIGHT_BRACKET_STR);
                String sub =  equation.substring(0,end);
                int start = sub.lastIndexOf(ZSSign.LEFT_BRACKET_STR);
                expression = equation.replace(start, end+1,calculate(sub.substring(start+1), scale, mode).toPlainString()).toString();
            }
            LinkedList<BigDecimal> value = new LinkedList<>();
            LinkedList<Character> symbol = new LinkedList<>();
            StringBuilder number = new StringBuilder();
            char[] cs = expression.toCharArray();
            if (cs[0]=='-'){
                number.append(0);
            }
            for (char c : cs) {
                if (Character.isDigit(c) || c == '.') {
                    number.append(c);
                } else {
                    if (c=='-' && number.isEmpty()){
                        number.append(c);
                    }else{
                        calculate(value,number.toString(),symbol,scale,mode);
                        number.setLength(0);
                        symbol.addLast(c);
                    }
                }
            }
            calculate(value,number.toString(),symbol,scale,mode);
            BigDecimal result = value.getFirst();
            for (int i=0;i<symbol.size();i++){
                result = calculate(result,symbol.get(i),value.get(i+1),scale,mode);
            }
            return result;
        }catch (Exception e){
            throw new JSRuleException(JSRuleMessage.read("illegal.expression"));
        }
    }

private static void calculate(LinkedList<BigDecimal> value,String number,LinkedList<Character> symbol,int scale, RoundingMode mode){
        if (!symbol.isEmpty()) {
            char lastSymbol = symbol.getLast();
            if (lastSymbol == '+' || lastSymbol == '-') {
                value.addLast(new BigDecimal(number));
            } else {
                value.addLast(calculate(value.removeLast(), symbol.removeLast(), new BigDecimal(number),scale,mode));
            }
        } else {
            value.addLast(new BigDecimal(number));
        }
    }

public static BigDecimal calculate(BigDecimal left, char operation, BigDecimal right, int scale, RoundingMode mode){
        if (operation== '+'){
            return left.add(right);
        }else if (operation=='-'){
            return left.subtract(right);
        }else if (operation=='*'){
            return left.multiply(right);
        }else if (operation=='/'){
            return left.divide(right,scale,mode);
        }else if (operation=='%'){
            return left.divideAndRemainder(right)[1];
        }
        throw new JSRuleException(JSRuleMessage.read("unsupported.arithmetic.type"),String.valueOf(operation));
    }

使用该方法可以用()括号,也可以是负数开头,只要你的表达式正确,程序能够处理的它也可以处理,唯一不同的是如果你写成这样,3.1(2+2),它也是允许的,结果为3.14

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九天流云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值