You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java多阶负号数学表达式求值器功能异常求助

修复Java数学表达式求值器的多阶负号处理问题

我开发的Java数学表达式求值器无法正确处理含多阶负号的表达式,具体错误示例:

  • 表达式1 - -(-(-(-4)))正确结果应为-3.0,但程序返回5
  • 表达式12*123/-(-5+2)正确结果是492,但程序返回-492

问题代码

import java.util.*;

public class MathEvaluator {

    // 获取运算符优先级
    public static int getPrecedence(Character op) {
        switch (op) {
            case '*':
                return 2;
            case '/':
                return 2;
            case '+':
                return 1;
            case '-':
                return 1;
            default:
                return 0;
        }
    }

    // 计算两个数的简单表达式(栈结构导致操作数顺序反转)
    public static double evalSimpleExpr(String operator, double a, double b) {
        switch (operator) {
            case "*":
                return b * a;
            case "/":
                if (a == 0)
                    return 0;
                return b / a;
            case "+":
                return b + a;
            case "-":
                return b - a;
            default:
                return 0;
        }
    }

    public static double calculate(String expression) {
        System.out.println("[DEBUG]: " + expression);
        // 移除所有空白字符
        char[] characters = expression.replaceAll("\\s+", "").toCharArray();

        List<String> output = new ArrayList<String>();
        Stack<Character> ops = new Stack<Character>();

        mainLoop: for (int i = 0; i < characters.length; i++) {

            // 处理数字和小数点
            if (Character.isDigit(characters[i]) || characters[i] == '.') {
                // 跳过连续的数字/小数点,避免重复处理
                if (i > 0 && (Character.isDigit(characters[i - 1]) || characters[i - 1] == '.'))
                    continue mainLoop;
                String digits = "";
                // 手动添加数字前的负号
                if (i > 1 && characters[i - 1] == '-'
                        && (characters[i - 2] == '+' || characters[i - 2] == '-' || characters[i - 2] == '*'
                                || characters[i - 2] == '/' || characters[i - 2] == '(' || characters[i - 2] == ')')) {
                    digits += "-";
                } else if (i == 1 && characters[0] == '-') {
                    digits += "-";
                }
                // 读取完整数字
                digits += characters[i];
                int j = i + 1;
                while (!(j >= characters.length) && (Character.isDigit(characters[j]) || characters[j] == '.')) {
                    digits += characters[j];
                    j += 1;
                }

                System.out.println("[NUMBER]: " + digits);
                output.add(digits);

            // 处理左括号
            } else if (characters[i] == '(') {
                ops.push(characters[i]);

            // 处理右括号
            } else if (characters[i] == ')') {
                while (!ops.empty() && ops.peek() != '(') {
                    output.add(Character.toString(ops.pop()));
                }
                ops.pop();

            // 处理运算符
            } else if (characters[i] == '+' || characters[i] == '-' || characters[i] == '*' || characters[i] == '/') {
                // 直接跳过负号
                if (i > 0 && characters[i] == '-' && (characters[i - 1] == '+' ||
                        characters[i - 1] == '-'
                        || characters[i - 1] == '*' || characters[i - 1] == '/' || characters[i - 1] == '(')) {
                    continue mainLoop;
                } else if (i == 0 && characters[i] == '-') {
                    continue mainLoop;
                }
                // 按优先级弹出运算符
                while (!ops.empty() && getPrecedence(ops.peek()) >= getPrecedence(characters[i])) {
                    output.add(Character.toString(ops.pop()));
                }
                ops.push(characters[i]);
            }
        }

        // 弹出剩余运算符
        while (!ops.empty()) {
            output.add(Character.toString(ops.pop()));
        }

        System.out.println(output);

        return postfixEval(output);
    }

    // 后缀表达式求值
    public static double postfixEval(List<String> postfix) {

        Stack<String> stack = new Stack<String>();

        for (String element : postfix) {
            // 处理二元运算符
            if (stack.size() >= 2
                    && (element.equals("+") || element.equals("-") || element.equals("*") || element.equals("/"))) {
                stack.push(Double.toString(
                        evalSimpleExpr(element, Double.parseDouble(stack.pop()), Double.parseDouble(stack.pop()))));
            } else {
                stack.push(element);
            }
        }

        System.out.println(stack);
        return Double.parseDouble(stack.pop());
    }

    // 测试
    public static void main(String[] args) {
        calculate("15+2*-12-(0.5+0.8*(8-1))/-0.1+0.5+0.5");
        calculate("12* 123/-(-5 + 2)");
    }

}

问题根源

代码核心问题是未区分二元减号(如a - b)和一元负号(如-x、--x):

  1. 直接跳过作为负号的-,未将其转化为有效的运算逻辑
  2. 仅处理了紧邻数字的单个负号,无法处理连续负号或嵌套在括号后的多阶负号

修复方案

我们需要给一元负号设置更高优先级,让它先于乘除运算执行,同时在后缀表达式求值时单独处理一元负号逻辑。

1. 修改优先级方法,支持区分一元/二元运算符

public static int getPrecedence(Character op, boolean isUnary) {
    if (isUnary) {
        return 3; // 一元负号优先级高于乘除
    }
    switch (op) {
        case '*':
        case '/':
            return 2;
        case '+':
        case '-':
            return 1;
        default:
            return 0;
    }
}

2. 调整运算符识别逻辑,标记一元负号

在calculate方法的运算符分支中,判断-是否为一元负号,并用特殊符号~标记(避免和二元减号混淆):

// 替换原运算符处理分支
} else if (characters[i] == '+' || characters[i] == '-' || characters[i] == '*' || characters[i] == '/') {
    boolean isUnary = false;
    if (characters[i] == '-') {
        // 判断是否为一元负号:表达式开头、左括号后、其他运算符后
        isUnary = (i == 0) || (characters[i-1] == '(') || 
                  (characters[i-1] == '+') || (characters[i-1] == '-') || 
                  (characters[i-1] == '*') || (characters[i-1] == '/');
    }

    if (isUnary) {
        // 处理一元负号,按高优先级入栈
        while (!ops.empty() && getPrecedence(ops.peek(), false) >= getPrecedence('~', true)) {
            output.add(Character.toString(ops.pop()));
        }
        ops.push('~');
    } else {
        // 处理二元运算符
        while (!ops.empty() && getPrecedence(ops.peek(), false) >= getPrecedence(characters[i], false)) {
            output.add(Character.toString(ops.pop()));
        }
        ops.push(characters[i]);
    }
}

3. 移除数字识别中手动添加负号的代码

删除数字分支中手动添加负号的逻辑,因为一元负号会作为独立运算符处理:

// 移除以下代码块
/*
if (i > 1 && characters[i - 1] == '-'
        && (characters[i - 2] == '+' || characters[i - 2] == '-' || characters[i - 2] == '*'
                || characters[i - 2] == '/' || characters[i - 2] == '(' || characters[i - 2] == ')')) {
    digits += "-";
} else if (i == 1 && characters[0] == '-') {
    digits += "-";
}
*/

4. 扩展表达式求值方法,支持一元负号

修改evalSimpleExpr方法,添加~(一元负号)的处理逻辑:

public static double evalSimpleExpr(String operator, double a, double b) {
    switch (operator) {
        case "*":
            return b * a;
        case "/":
            if (a == 0)
                return 0;
            return b / a;
        case "+":
            return b + a;
        case "-":
            return b - a;
        case "~": // 一元负号,仅需一个操作数
            return -a;
        default:
            return 0;
    }
}

5. 调整后缀表达式求值逻辑,处理一元运算符

修改postfixEval方法,单独处理一元负号(只需要一个操作数):

public static double postfixEval(List<String> postfix) {
    Stack<String> stack = new Stack<String>();
    for (String element : postfix) {
        if (element.equals("~")) {
            // 一元负号:弹出一个数取反后压栈
            double num = Double.parseDouble(stack.pop());
            stack.push(Double.toString(-num));
        } else if (stack.size() >= 2
                && (element.equals("+") || element.equals("-") || element.equals("*") || element.equals("/"))) {
            stack.push(Double.toString(
                    evalSimpleExpr(element, Double.parseDouble(stack.pop()), Double.parseDouble(stack.pop()))));
        } else {
            stack.push(element);
        }
    }
    return Double.parseDouble(stack.pop());
}

验证修复

修改后运行测试用例:

  • 1 - -(-(-(-4)))返回正确结果-3.0
  • 12*123/-(-5+2)返回正确结果492

内容的提问来源于stack exchange,提问作者TommyYOyoyo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 20:12:34