Method for Evaluating Math Expressions in Java

How to evaluate a math expression given in string form?

With JDK1.6, you can use the built-in Javascript engine.

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;

public class Test {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String foo = "40+2";
System.out.println(engine.eval(foo));
}
}

evaluating expressions containing functions in java

May be use JavaScript interpreter?

First create engine instance and init:

// Manager creates engines by mime/language names.
// It has own global scope for engiges created by it.
ScriptEngineManager manager = new ScriptEngineManager();
// Create JavaScript interpreter instance.
// (Nashorn is bundled JavaScript interpreter)
ScriptEngine scope = manager.getEngineByName("JavaScript");
// Define functions you need
String initialScript = "cos = Math.cos;" // ; or \n
+ "sin = Math.sin;"
+ "tg = Math.tan;"
+ "PI = Math.PI;"
// Any other function
+ "ctg = function (x) { return cos(x)/sin(x); };";
// ...

try {
// Add these functions to scope
scope.eval(initialScript);
} catch(ScriptException ex) {
// Evaluating exceptions, syntax errors are thrown here
}

And then you can evaluate expressions in the "scope" many times:

try {
double d = (double)scope.eval("sin(PI/2) + cos(PI/2)");
System.out.println("Calculated: " + d);
} catch(ScriptException e) {
// ...
}

Be warned:

  1. There is language interpreting - user can pass any script and...
  2. ... it can reduce perfomance of application.

You can also use, for example, Jython or JRuby as interpreter.

Evaluating a math expression with variables. (java 8)

Sorry for the confusion. The "parse" function I referred to is simply the existing eval function, but renamed since it returns an Expression object.

So you'd have:

public static Expression parse(String str, Map<String,Double> variables) { ... }

And invoke it by:

Map<String,Double> variables = new HashMap<>();
Expression exp = parse("x+(sqrt(x))", variables);
for (double x = 100; x <= +120; x++) {
variables.put("x", x);
System.out.println(x + " => " + exp.eval());
}

One other thing: It's necessary to know at parse time whether a name refers to a variable or a function, in order to know whether or not it takes an argument, but you can't call containsKey on the variables map during the parse, since the variables might not be present in the map until exp.eval() is called! One solution is to put functions in a map instead, so you can call containsKey on that:

    } else if (ch >= 'a' && ch <= 'z') { // functions and variables
while (ch >= 'a' && ch <= 'z') nextChar();
String name = str.substring(startPos, this.pos);
if (functions.containsKey(name)) {
DoubleUnaryOperator func = functions.get(name);
Expression arg = parseFactor();
x = () -> func.applyAsDouble(arg.eval());
} else {
x = () -> variables.get(name);
}
} else {

And then somewhere at class level, initialize the functions map:

private static final Map<String,DoubleUnaryOperator> functions = new HashMap<>();
static {
functions.put("sqrt", x -> Math.sqrt(x));
functions.put("sin", x -> Math.sin(Math.toRadians(x)));
functions.put("cos", x -> Math.cos(Math.toRadians(x)));
functions.put("tan", x -> Math.tan(Math.toRadians(x)));
}

(It would also be okay to define the functions map as a local variable inside the parser, but that adds a little bit more overhead during each parse.)

Creating a calculator to evaluate arithmetic expressions in Java - code troubles

The main reason you algorithm fails is due to using == when trying to check for String equality.

In Java, == is a boolean operator which behaves identically for all operands, and checks the equality of the values of the operands. This means that primitives are checked as one might expect, but Strings, which are objects, will result in comparing the memory references of the two Strings, which will result in true only if the two Strings are actually the same String. This means that String equality checks must be done with the equals method.

There are more issues with the behavior of the calculator (algorithmic ones), but those will be easier to identify and fix after handling the String equality checks. One example of an issue that must be fixed is:

 while(!ops.isEmpty() && nums.size() > 1)
{
String ab = ops.pop();
double bb = nums.pop();
double cb = nums.pop();
double clac = applyOperator(bb,ab,cb);
nums.add(clac);
}

The operands (bb and cb) are popped from a Stack, therefore they arrive in a reveresed order (when parsed, cb was pushed into the stack before bb). This means that cb is the leftside operand and bb is the rightside operand -> double clac = applyOperator(cb,ab,bb); The same refactoring should be done for all usages of the applyOperand method.

Another issue is the following:

        else if (prec(tokens [i]) < prec(ops.peek()) && !ops.isEmpty() && ops.peek() != "(")
{
String ac1 = ops.pop();
double res1 = nums.pop();
double res2 = nums.pop();
double outcome = applyOperator(res1,ac1,res2);
nums.add(outcome);
}

An internal evaluation was made, but the triggering of the evaluation is the discvery of an operand with a lower presendence. The operand should be pushed into the operations stack after the evaluation:

 else if (prec(tokens [i]) < prec(ops.peek()) && !ops.isEmpty() && ops.peek() != "(")
{
...
...
nums.add(outcome); // I highly suggest refactoring this to nums.push due to readability considerations
ops.push(tokens[i]);
}

References:

  • The difference between == and equals in Java
  • Guide for implementing a scientific calculator (in c++, use as an algorithmic reference)
  • The Shunting Yard Algorithm - As suggested by user207421

Expression evaluation in java

From Java 6, you could use the built-in Javascript engine.

ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine eng = sem.getEngineByName("JavaScript");
String str = "5*8-9/7+5-8";
System.out.println(eng.eval(str));

Note: The eval() method throws a ScriptException and you'll also need the below imports.

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;


Related Topics



Leave a reply



Submit