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. 修改优先级方法,支持区分一元/二元运算符
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.012*123/-(-5+2)返回正确结果492
内容的提问来源于stack exchange,提问作者TommyYOyoyo
相关产品推荐
相关产品推荐

