I've been trying to make a mathematical expression evaluator program (calculator) in java and I'm stuck with a problem: I can't find a way to make the program support mathematical expressions with multi-levels of negative signs.
The expression
1 - -(-(-(-4)))
's result should be -3.0, but I'm unable to make my program determine whether the number "4" is a negative number or not. My program returns 5 instead since it cannot determine correctly.
Another example is the expression
12*123/-(-5+2)
It should return an answer of 492, but my program incorrectly returns -492.
Here's the code I've been working on:
import java.util.*;
public class MathEvaluator {
// get the precedence order of an operator
public static int getPrecedence(Character op) {
switch (op) {
case '*':
return 2;
case '/':
return 2;
case '+':
return 1;
case '-':
return 1;
default:
return 0;
}
}
// evaluating simple math expressions between two floating point numbers
// order is reversed due to the stack sturcture (LIFO), b comes first while a
// comes after
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);
// make a new array of characters while removing all the whitespaces
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 element is an integer / floating-point number / decimal point, then
// push it into the stack "numbers"
if (Character.isDigit(characters[i]) || characters[i] == '.') {
// if the character before the current element is still a digit/decimal point,
// then ignore and continue scanning the next element
if (i > 0 && (Character.isDigit(characters[i - 1]) || characters[i - 1] == '.'))
continue mainLoop;
String digits = "";
// get the negative sign (if it exists for the number) and add it to the
// number
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 += "-";
}
// a loop to the right to get all digits in a number
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);
// check if element is brace
// if its an opening brace, push it into the operator stack
} else if (characters[i] == '(') {
ops.push(characters[i]);
// if its a closing brace, pop all the elements between the braces into the
// output queue one by one
} else if (characters[i] == ')') {
while (!ops.empty() && ops.peek() != '(') {
output.add(Character.toString(ops.pop()));
}
ops.pop();
// check if element is an operator
} else if (characters[i] == '+' || characters[i] == '-' || characters[i] == '*' || characters[i] == '/') {
// ignoring negative signs
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;
}
/*
* check if the previous operator has priority over the current operator or if
* the previous operator has the same priority as the current operator (if they
* have the same priority,
* then the previous operator should be applied before the current operator)
* and pop the elements from the stack until the current operator takes the
* priority
*/
while (!ops.empty() && getPrecedence(ops.peek()) >= getPrecedence(characters[i])) {
output.add(Character.toString(ops.pop()));
}
ops.push(characters[i]);
}
}
// push the rest of the operators from the stack to the output queue
while (!ops.empty()) {
output.add(Character.toString(ops.pop()));
}
System.out.println(output);
return postfixEval(output);
}
// evaluating the postfix expression
public static double postfixEval(List<String> postfix) {
Stack<String> stack = new Stack<String>();
for (String element : postfix) {
// if the element is an operator and the stack has more than two elements, apply
// the operator to the two last numbers from the stack
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());
}
// test
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)");
}
}
Thanks!