Categories
java math string

How to evaluate a math expression given in string form?

374

I’m trying to write a Java routine to evaluate math expressions from String values like:

  1. "5+3"
  2. "10-40"
  3. "(1+10)*3"

I want to avoid a lot of if-then-else statements.
How can I do this?

7

411

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));
    } 
}

20

  • 60

    It seems there’s a major problem there; It executes a script, not evaluates an expression. To be clear, engine.eval(“8;40+2”), outputs 42 ! If you want an expression parser that also check the syntax, I’ve just finished one (because I found nothing that suits my needs) : Javaluator.

    Aug 29, 2012 at 12:33


  • 4

    As a side note, if you need to use the result of this expression elsewhere in your code, you can typecast the result to a Double like so: return (Double) engine.eval(foo);

    Apr 2, 2014 at 22:44


  • 54

    Security note: You should never use this in a server context with user input. The executed JavaScript can access all Java classes and thus hijack your application without limit.

    – Boann

    Sep 21, 2015 at 11:08

  • 5

    @Boann, I request you to give me a reference about what you said.(to be sure 100%)

    – partho

    Feb 27, 2016 at 13:03


  • 28

    @partho new javax.script.ScriptEngineManager().getEngineByName("JavaScript") .eval("var f = new java.io.FileWriter('hello.txt'); f.write('UNLIMITED POWER!'); f.close();"); — will write a file via JavaScript into (by default) the program’s current directory

    – Boann

    Feb 27, 2016 at 13:37


293

I’ve written this eval method for arithmetic expressions to answer this question. It does addition, subtraction, multiplication, division, exponentiation (using the ^ symbol), and a few basic functions like sqrt. It supports grouping using (), and it gets the operator precedence and associativity rules correct.

public static double eval(final String str) {
    return new Object() {
        int pos = -1, ch;
        
        void nextChar() {
            ch = (++pos < str.length()) ? str.charAt(pos) : -1;
        }
        
        boolean eat(int charToEat) {
            while (ch == ' ') nextChar();
            if (ch == charToEat) {
                nextChar();
                return true;
            }
            return false;
        }
        
        double parse() {
            nextChar();
            double x = parseExpression();
            if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
            return x;
        }
        
        // Grammar:
        // expression = term | expression `+` term | expression `-` term
        // term = factor | term `*` factor | term `/` factor
        // factor = `+` factor | `-` factor | `(` expression `)` | number
        //        | functionName `(` expression `)` | functionName factor
        //        | factor `^` factor
        
        double parseExpression() {
            double x = parseTerm();
            for (;;) {
                if      (eat('+')) x += parseTerm(); // addition
                else if (eat('-')) x -= parseTerm(); // subtraction
                else return x;
            }
        }
        
        double parseTerm() {
            double x = parseFactor();
            for (;;) {
                if      (eat('*')) x *= parseFactor(); // multiplication
                else if (eat("https://stackoverflow.com/")) x /= parseFactor(); // division
                else return x;
            }
        }
        
        double parseFactor() {
            if (eat('+')) return +parseFactor(); // unary plus
            if (eat('-')) return -parseFactor(); // unary minus
            
            double x;
            int startPos = this.pos;
            if (eat('(')) { // parentheses
                x = parseExpression();
                if (!eat(')')) throw new RuntimeException("Missing ')'");
            } else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
                while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
                x = Double.parseDouble(str.substring(startPos, this.pos));
            } else if (ch >= 'a' && ch <= 'z') { // functions
                while (ch >= 'a' && ch <= 'z') nextChar();
                String func = str.substring(startPos, this.pos);
                if (eat('(')) {
                    x = parseExpression();
                    if (!eat(')')) throw new RuntimeException("Missing ')' after argument to " + func);
                } else {
                    x = parseFactor();
                }
                if (func.equals("sqrt")) x = Math.sqrt(x);
                else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
                else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
                else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
                else throw new RuntimeException("Unknown function: " + func);
            } else {
                throw new RuntimeException("Unexpected: " + (char)ch);
            }
            
            if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation
            
            return x;
        }
    }.parse();
}

Example:

System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));

Output: 7.5 (which is correct)


The parser is a recursive descent parser, so internally uses separate parse methods for each level of operator precedence in its grammar. I deliberately kept it short, but here are some ideas you might want to expand it with:

  • Variables:

    The bit of the parser that reads the names for functions can easily be changed to handle custom variables too, by looking up names in a variable table passed to the eval method, such as a Map<String,Double> variables.

  • Separate compilation and evaluation:

    What if, having added support for variables, you wanted to evaluate the same expression millions of times with changed variables, without parsing it every time? It’s possible. First define an interface to use to evaluate the precompiled expression:

      @FunctionalInterface
      interface Expression {
          double eval();
      }
    

    Now to rework the original “eval” function into a “parse” function, change all the methods that return doubles, so instead they return an instance of that interface. Java 8’s lambda syntax works well for this. Example of one of the changed methods:

      Expression parseExpression() {
          Expression x = parseTerm();
          for (;;) {
              if (eat('+')) { // addition
                  Expression a = x, b = parseTerm();
                  x = (() -> a.eval() + b.eval());
              } else if (eat('-')) { // subtraction
                  Expression a = x, b = parseTerm();
                  x = (() -> a.eval() - b.eval());
              } else {
                  return x;
              }
          }
      }
    

    That builds a recursive tree of Expression objects representing the compiled expression (an abstract syntax tree). Then you can compile it once and evaluate it repeatedly with different values:

      public static void main(String[] args) {
          Map<String,Double> variables = new HashMap<>();
          Expression exp = parse("x^2 - x + 2", variables);
          for (double x = -20; x <= +20; x++) {
              variables.put("x", x);
              System.out.println(x + " => " + exp.eval());
          }
      }
    
  • Different datatypes:

    Instead of double, you could change the evaluator to use something more powerful like BigDecimal, or a class that implements complex numbers, or rational numbers (fractions). You could even use Object, allowing some mix of datatypes in expressions, just like a real programming language. 🙂


All code in this answer released to the public domain. Have fun!

18

  • 3

    Nice algorithm, starting from it I managed to impliment and logical operators. We created separate classes for functions to evaluate a function, so like your idea of variables, I create a map with functions and looking after the function name. Every function implements an interface with a method eval (T rightOperator , T leftOperator), so anytime we can add features without changing the algorithm code. And it is a good idea to make it work with generic types. Thanks you!

    Jul 23, 2016 at 16:17

  • 1

    Can you explain the logic behind this algorithm?

    – iYonatan

    Jul 24, 2016 at 0:06

  • 1

    I try to give a description of what I understand from the code written by Boann, and examples described wiki.The logic of this algoritm starting from rules of operation orders. 1. operator sign | variable evaluation | function call | parenthesis (sub-expressions); 2. exponentiation; 3. multiplication, division; 4. addition, subtraction;

    Jul 24, 2016 at 15:05


  • 1

    Algorithm methods are divided for each level of operations order as follows: parseFactor = 1. operator sign | variable evaluation | function call | parenthesis (sub-expressions); 2. exponentiation; parseTerms = 3. multiplication, division; parseExpression = 4. addition, subtraction. The algorithm, call methods in reverse order (parseExpression -> parseTerms -> parseFactor -> parseExpression (for sub-expressions)), but every method to the first line call the method to the next level, so the entire execution order methods will be actually normal order of operations.

    Jul 24, 2016 at 15:09

  • 1

    For example the parseExpression method the double x = parseTerm(); evaluate the left operator, after this for (;;) {...} evaluate succesive operations of actual order level (addition, subtraction). The same logic are and in parseTerm method. The parseFactor does not have next level, so there are only evaluations of methods/ variables or in case of paranthesis – evaluate sub-expression. The boolean eat(int charToEat) method check equality of the current cursor character with the charToEat character, if equal return true and move cursor to next character, I use name ‘accept’ for it.

    Jul 24, 2016 at 15:12


42

For my university project, I was looking for a parser / evaluator supporting both basic formulas and more complicated equations (especially iterated operators). I found very nice open source library for JAVA and .NET called mXparser. I will give a few examples to make some feeling on the syntax, for further instructions please visit project website (especially tutorial section).

https://mathparser.org/

https://mathparser.org/mxparser-tutorial/

https://mathparser.org/api/

And few examples

1 – Simple furmula

Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()

2 – User defined arguments and constants

Argument x = new Argument("x = 10");
Constant a = new Constant("a = pi^2");
Expression e = new Expression("cos(a*x)", x, a);
double v = e.calculate()

3 – User defined functions

Function f = new Function("f(x, y, z) = sin(x) + cos(y*z)");
Expression e = new Expression("f(3,2,5)", f);
double v = e.calculate()

4 – Iteration

Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()

Found recently – in case you would like to try the syntax (and see the advanced use case) you can download the Scalar Calculator app that is powered by mXparser.

4

  • 1

    So far this is the best math library out there; simple to kickstart, easy to use and extendable. Definitely should be top answer.

    Feb 6, 2019 at 7:00

  • 2

    Find Maven version here.

    – izogfif

    Mar 12, 2019 at 5:36

  • 1

    I found mXparser can’t identify illegal formula, for example, ‘0/0’ will get a result as ‘0’. How can I solve this problem?

    – lulijun

    Mar 14, 2019 at 6:41

  • 2

    Just found the solution, expression.setSlientMode()

    – lulijun

    Mar 14, 2019 at 7:07