385

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

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

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

Pshemo
  • 122,468
  • 25
  • 185
  • 269
Shah
  • 4,878
  • 8
  • 25
  • 33
  • 2
    What kinds of expressions do you permit? Only single operator expressions? Are parentheses permitted? – Raedwald Dec 10 '14 at 13:20
  • 10
    I recently wrote a math expression parser called exp4j that was released under the apache license you can check it out here: http://www.objecthunter.net/exp4j/ – fasseg Dec 29 '10 at 12:21
  • 4
    Also take a look at [Dijkstra's two-stack algorithm](http://algs4.cs.princeton.edu/13stacks/Evaluate.java.html) – Ritesh Jan 21 '16 at 15:33
  • 1
    Possible duplicate of [Is there an eval() function in Java?](http://stackoverflow.com/questions/2605032/is-there-an-eval-function-in-java) – Andrew Li Sep 20 '16 at 23:24
  • 1
    @fasseg, the library is pretty awesome, but, it seems like it only deals with numerical expressions, please correct me if I'm wrong. I have a requirement where I need to check the equality of strings in an expression, can you please suggest! – Nagendra Varma Jul 30 '17 at 07:00
  • 3
    How can this possible be considered too broad? Dijkstra's evaluation is the obvious solution here https://en.wikipedia.org/wiki/Shunting-yard_algorithm – Martin Spamer Oct 13 '18 at 09:43
  • 1
    Since java 9 there is JShell JShell shell = JShell.create(); shell.eval("...") – Joop Eggen Oct 22 '19 at 06:22

27 Answers27

416

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));
    } 
}
Abdull
  • 26,371
  • 26
  • 130
  • 172
RealHowTo
  • 34,977
  • 11
  • 70
  • 85
  • 61
    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](http://javaluator.sourceforge.net). – Jean-Marc Astesana Aug 29 '12 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);` – Ben Visness Apr 02 '14 at 22:44
  • 57
    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 '15 at 11:08
  • 1
    what's the meaning of `getEngineByName("JavaScript")` here? – partho Jan 04 '16 at 13:42
  • 1
    @partho, getEngineByName() is used to look up and creates a ScriptEngine for a given name (ex. js, rhino, JavaScript, javascript, ECMAScript, ecmascript). See https://docs.oracle.com/javase/7/docs/api/javax/script/ScriptEngineManager.html – RealHowTo Jan 04 '16 at 22:08
  • 5
    @Boann, I request you to give me a reference about what you said.(to be sure 100%) – partho Feb 27 '16 at 13:03
  • 31
    @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 '16 at 13:37
  • 3
    Also be aware of floating-point math. 0.1+0.2 will evaluate to 0.30000000000000004 – Christian Kuetbach Mar 22 '16 at 15:19
  • 4
    Just note this is 100% open to denial-of-service if the string input isn't filtered. Like if `exit();` was passed it will exit in java. – php_coder_3809625 Apr 30 '16 at 08:40
  • 1
    @ChristianKuetbach That's a property of floating-point, not of executing scripts. – user207421 Jul 18 '16 at 00:23
  • 2
    @EJP: Thats true. But a lot of people are not aware, that JavaScript is floating-point only. I think, using JjavaScript for calculations is a bad idea. – Christian Kuetbach Jul 18 '16 at 12:04
  • 1
    In case of divide by zero it dosen`t throw exception and because it uses java script engine it results "infinity" – Mr.Q Jan 04 '17 at 09:42
  • 1
    @Jean-MarcAstesana how do you pass square root operations? Also, I passed `4^13` and it returned `6.7108864E7` :/ – Cardinal System Oct 01 '17 at 21:11
  • 2
    @CardinalSystem I do not understand what the problem is? 4^13 is equals to 6.7108864E7 ... or java method Math.pow is buggy. The squareroot of x is x^0.5. Feel free to extend javaluator for your own needs if needed, there's a tutorial on javaluator's site. – Jean-Marc Astesana Oct 02 '17 at 16:44
  • 3
    You can create a RegEx to evaluate an expression with Math chars only, like `Pattern mathExpressionPattern = Pattern.compile( "[0-9,.\\(\\)\\/\\-\\+\\*\\^]+" )` – JorgeGarza Oct 20 '18 at 01:33
  • 1
    Be aware, it will not work in the case of left zeros, `"024 + 862"` will produce `882`, not `886` – Ahmed Nabil Jul 14 '20 at 20:00
  • 2
    @ahmednabil88 , it's because in javascript "0" + number is an octal number, so 024 is interpreted as 20 in decimal in the expression "024 + 862" – RealHowTo Jul 15 '20 at 00:33
  • 1
    Nashorn (used in this solution) is being deprecated. I wouldn't recommend this solution for new code. – pmdci Oct 11 '21 at 10:36
  • https://stackoverflow.com/questions/70781378/consistent-evaluation-and-match-the-fractional-values – RRM Jan 20 '22 at 17:00
  • @JorgeGarza Be very careful with this type of thing. It is possible to execute arbitrarily powerful JavaScript with [quite limited characters](https://github.com/aemkei/jsfuck). I believe your example is safe because it doesn't allow `[` and `]`, but still it makes me uncomfortable to see user input passed to a full script engine. – Boann Jan 23 '22 at 06:28
  • Java 8: Cannot invoke "javax.script.ScriptEngine.eval(String)" because "engine" is null – JRichardsz Jan 31 '23 at 05:24
  • Note that this is kind of the "how do I drive to the bank?" / "have someone else drive you" version of an answer though, in that it doesn't explain anything about how to actually write the code to do this. If you turn this in as homework, it's a fail because you didn't do the exercise, and if you try to land this in a real project, it'll most certainly get shot down as you filing a joke PR/MR instead of adding an expression parser to the code base. It's useful to know eval exists, but it's a bit odd that this is the accepted answer =) – Mike 'Pomax' Kamermans Apr 08 '23 at 16:32
312

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('/')) 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!

Boann
  • 48,794
  • 16
  • 117
  • 146
  • 4
    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! – Vasile Bors Jul 23 '16 at 16:17
  • 1
    Can you explain the logic behind this algorithm? – iYonatan Jul 24 '16 at 00: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; – Vasile Bors Jul 24 '16 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. – Vasile Bors Jul 24 '16 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. – Vasile Bors Jul 24 '16 at 15:12
  • 1
    How can I modify the method which will be able to do this: 2(3+2) or (50+1)6 or 8(2)9 – iYonatan Jul 28 '16 at 12:46
  • 1
    Im attempting to follow your steps on how to alter the algorithm to accept variables. I converted all the functions that return doubles to return expression, but i dont understand what and how `Expression exp = parse("x^2 - x + 2", variables);` should work since parse was never declared. Also in parseFactor i used `else if (variables.containsKey(func)){x = () -> variables.get(func);}` to return my variable value but the code breaks recursion after the next numeric value. Any help is appreciated. – Hozeis Dec 02 '16 at 13:36
  • 1
    I have asked a question referencing you answer - http://stackoverflow.com/q/40975678/4825796 – Hozeis Dec 05 '16 at 15:30
  • 2
    Thank you for the snippet! Based on that I created a parser that can compare expressions with =, <, >, !=, etc. and can apply logical operators AND and OR too. – janhink Jan 04 '17 at 13:08
  • @Boann - thank you very much. I ported this code to Typescript. – tm1701 Mar 22 '19 at 18:26
  • 1
    +1, but there is a problem with parentheses. Your parser allows expressions without closed parentheses , like : `(7-2*2 `. You should change line `eat(')');` . – Pedja Feb 14 '20 at 05:31
  • 1
    @PeđaTerzić, good point. It can be fixed by changing `eat(')');` to `if (!eat(')')) throw new RuntimeException("Missing closing parenthesis");` – Matias Kinnunen Feb 22 '20 at 06:14
  • 1
    Woah, this is the first time I know about `for(;;)`... – Skaldebane Jul 15 '20 at 22:31
  • 1. Can you help me with a similar kind of Mathematical Expression question stackoverflow.com/questions/6666169/…  . 2. @Boann The public domain does not have any code – deepakl.2000 Oct 13 '21 at 19:57
  • `eval("12*0.0254")` evaluates incorrectly to `0.30479999999999996` – RRM Jan 20 '22 at 17:29
  • @RRM Just normal binary floating-point arithmetic. You could change it to BigDecimal if needed. – Boann Jan 20 '22 at 18:00
  • @Boann I've the String expressions as inputs. So, I must evaluate and have correct representation. – RRM Jan 20 '22 at 20:24
  • @RRM Yes, just change the evaluation type from double to BigDecimal or some type of fraction number class. – Boann Jan 21 '22 at 03:24
  • @Boann if the expression contains signed number for example -1+7 in this extracting -1 is very complex. another example +10-89.Please help.Actually i used above parsing technique along with shunting yard algorithm to construct tree and evaluate..It works very well,i have wrote 69 test cases... but i am stuck in extracting signed number. Here is my entire working code,I have enjoyed developing this https://github.com/nagaraj200788/JavaExpressionEvaluator – Nagappa L M Oct 31 '22 at 12:59
45

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.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Leroy Kegan
  • 1,156
  • 10
  • 9
36

The correct way to solve this is with a lexer and a parser. You can write simple versions of these yourself, or those pages also have links to Java lexers and parsers.

Creating a recursive descent parser is a really good learning exercise.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
22

HERE is another open source library on GitHub named EvalEx.

Unlike the JavaScript engine this library is focused in evaluating mathematical expressions only. Moreover, the library is extensible and supports use of boolean operators as well as parentheses.

Tanvir
  • 542
  • 7
  • 16
  • This is ok , but fails when we try to multiply values of multiples of 5 or 10 , for example 65 * 6 results in 3.9E+2 ... – paarth batra Apr 26 '18 at 08:09
  • .But there is a way to fix this by casting it to int i.e. int output = (int) 65*6 it will result now 390 – paarth batra Apr 26 '18 at 11:31
  • 1
    To clarify, that is not a problem of the library but rather an issue with the representation of numbers as floating point values. – DavidBittner Jun 06 '18 at 15:52
  • This library is really good. @paarth batra Casting to int will remove all decimal points. Use this instead: expression.eval().toPlainString(); – einUsername Mar 06 '20 at 17:33
17

You can also try the BeanShell interpreter:

Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));
marciowerner
  • 508
  • 2
  • 7
  • 7
  • 1
    Can you please tell me how to use BeanShell in adnroid Studio. – Hanni Feb 06 '16 at 23:55
  • 1
    Hanni - this post might help you adding BeanShell to your androidstudio project: http://stackoverflow.com/questions/18520875/adding-external-jar-to-androidstudio-project – marciowerner Feb 08 '16 at 04:23
16

Another way is to use the Spring Expression Language or SpEL which does a whole lot more along with evaluating mathematical expressions, therefore maybe slightly overkill. You do not have to be using Spring framework to use this expression library as it is stand-alone. Copying examples from SpEL's documentation:

ExpressionParser parser = new SpelExpressionParser();
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); //24.0
ax.
  • 58,560
  • 8
  • 81
  • 72
Faheem Sohail
  • 806
  • 8
  • 21
16

You can evaluate expressions easily if your Java application already accesses a database, without using any other JARs.

Some databases require you to use a dummy table (eg, Oracle's "dual" table) and others will allow you to evaluate expressions without "selecting" from any table.

For example, in Sql Server or Sqlite

select (((12.10 +12.0))/ 233.0) amount

and in Oracle

select (((12.10 +12.0))/ 233.0) amount from dual;

The advantage of using a DB is that you can evaluate many expressions at the same time. Also most DB's will allow you to use highly complex expressions and will also have a number of extra functions that can be called as necessary.

However performance may suffer if many single expressions need to be evaluated individually, particularly when the DB is located on a network server.

The following addresses the performance problem to some extent, by using a Sqlite in-memory database.

Here's a full working example in Java

Class. forName("org.sqlite.JDBC");
Connection conn = DriverManager.getConnection("jdbc:sqlite::memory:");
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount");
rs.next();
System.out.println(rs.getBigDecimal(1));
stat.close();
conn.close();

Of course you could extend the above code to handle multiple calculations at the same time.

ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");
DAB
  • 1,631
  • 19
  • 25
  • 10
    Say hello to SQL injection! – cyberz Apr 23 '17 at 19:53
  • It depends on what you use the DB for. If you want to be sure, you could easily create an empty sqlite DB, specifically for the the maths evaluation. – DAB Apr 24 '17 at 15:06
  • 4
    @cyberz If you use my example above, Sqlite will create a temporary DB in memory. See http://stackoverflow.com/questions/849679/temporary-in-memory-database-in-sqlite – DAB Apr 24 '17 at 15:09
11

This article discusses various approaches. Here are the 2 key approaches mentioned in the article:

JEXL from Apache

Allows for scripts that include references to java objects.

// Create or retrieve a JexlEngine
JexlEngine jexl = new JexlEngine();
// Create an expression object
String jexlExp = "foo.innerFoo.bar()";
Expression e = jexl.createExpression( jexlExp );
 
// Create a context and add data
JexlContext jctx = new MapContext();
jctx.set("foo", new Foo() );
 
// Now evaluate the expression, getting the result
Object o = e.evaluate(jctx);

Use the javascript engine embedded in the JDK:

private static void jsEvalWithVariable()
{
    List<String> namesList = new ArrayList<String>();
    namesList.add("Jill");
    namesList.add("Bob");
    namesList.add("Laureen");
    namesList.add("Ed");
 
    ScriptEngineManager mgr = new ScriptEngineManager();
    ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
 
    jsEngine.put("namesListKey", namesList);
    System.out.println("Executing in script environment...");
    try
    {
      jsEngine.eval("var x;" +
                    "var names = namesListKey.toArray();" +
                    "for(x in names) {" +
                    "  println(names[x]);" +
                    "}" +
                    "namesListKey.add(\"Dana\");");
    }
    catch (ScriptException ex)
    {
        ex.printStackTrace();
    }
}
Community
  • 1
  • 1
Brad Parks
  • 66,836
  • 64
  • 257
  • 336
9

if we are going to implement it then we can can use the below algorithm :--

  1. While there are still tokens to be read in,

    1.1 Get the next token. 1.2 If the token is:

    1.2.1 A number: push it onto the value stack.

    1.2.2 A variable: get its value, and push onto the value stack.

    1.2.3 A left parenthesis: push it onto the operator stack.

    1.2.4 A right parenthesis:

     1 While the thing on top of the operator stack is not a 
       left parenthesis,
         1 Pop the operator from the operator stack.
         2 Pop the value stack twice, getting two operands.
         3 Apply the operator to the operands, in the correct order.
         4 Push the result onto the value stack.
     2 Pop the left parenthesis from the operator stack, and discard it.
    

    1.2.5 An operator (call it thisOp):

     1 While the operator stack is not empty, and the top thing on the
       operator stack has the same or greater precedence as thisOp,
       1 Pop the operator from the operator stack.
       2 Pop the value stack twice, getting two operands.
       3 Apply the operator to the operands, in the correct order.
       4 Push the result onto the value stack.
     2 Push thisOp onto the operator stack.
    
  2. While the operator stack is not empty, 1 Pop the operator from the operator stack. 2 Pop the value stack twice, getting two operands. 3 Apply the operator to the operands, in the correct order. 4 Push the result onto the value stack.

  3. At this point the operator stack should be empty, and the value stack should have only one value in it, which is the final result.

Prashant Gautam
  • 589
  • 8
  • 10
  • 5
    This is an uncredited exposition of the [Dijkstra Shunting-yard algorithm](https://en.wikipedia.org/wiki/Shunting-yard_algorithm). Credit where credit is due. – user207421 Jun 16 '16 at 03:51
8

This is another interesting alternative https://github.com/Shy-Ta/expression-evaluator-demo

The usage is very simple and gets the job done, for example:

  ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");  
  assertEquals(BigDecimal.valueOf(11), evalExpr.eval()); 
Scorpion
  • 3,938
  • 24
  • 37
6

It seems like JEP should do the job

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
5

It's too late to answer but I came across same situation to evaluate expression in java, it might help someone

MVEL does runtime evaluation of expressions, we can write a java code in String to get it evaluated in this.

    String expressionStr = "x+y";
    Map<String, Object> vars = new HashMap<String, Object>();
    vars.put("x", 10);
    vars.put("y", 20);
    ExecutableStatement statement = (ExecutableStatement) MVEL.compileExpression(expressionStr);
    Object result = MVEL.executeExpression(statement, vars);
Saravana
  • 12,647
  • 2
  • 39
  • 57
5

Try the following sample code using JDK1.6's Javascript engine with code injection handling.

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

public class EvalUtil {
private static ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
public static void main(String[] args) {
    try {
        System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || 5 >3 "));
        System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || true"));
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public Object eval(String input) throws Exception{
    try {
        if(input.matches(".*[a-zA-Z;~`#$_{}\\[\\]:\\\\;\"',\\.\\?]+.*")) {
            throw new Exception("Invalid expression : " + input );
        }
        return engine.eval(input);
    } catch (Exception e) {
        e.printStackTrace();
        throw e;
    }
 }
}
Bruce
  • 793
  • 8
  • 17
5

This is actually complementing the answer given by @Boann. It has a slight bug which causes "-2 ^ 2" to give an erroneous result of -4.0. The problem for that is the point at which the exponentiation is evaluated in his. Just move the exponentiation to the block of parseTerm(), and you'll be all fine. Have a look at the below, which is @Boann's answer slightly modified. Modification is in the comments.

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 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('/')) x /= parseFactor(); // division
                else if (eat('^')) x = Math.pow(x, parseFactor()); //exponentiation -> Moved in to here. So the problem is fixed
                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();
                eat(')');
            } 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);
                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 -> This is causing a bit of problem

            return x;
        }
    }.parse();
}
Community
  • 1
  • 1
Romeo Sierra
  • 1,666
  • 1
  • 17
  • 35
  • 1
    `-2^2 = -4` is actually normal, and not a bug. It gets grouped like `-(2^2)`. [Try it on Desmos, for example.](https://www.desmos.com/calculator/jehnsuvt0w) Your code actually introduces several bugs. The first is that `^` no longer groups right-to-left. In other words, `2^3^2` is supposed to group like `2^(3^2)` because `^` is right-associative, but your modifications make it group like `(2^3)^2`. The second is that `^` is supposed to have higher precedence than `*` and `/`, but your modifications treat it the same. See https://ideone.com/iN2mMa. – Radiodef Aug 16 '18 at 01:06
  • So, what you are suggesting is that exponentiation is better kept at where it was isn't it? – Romeo Sierra Aug 16 '18 at 05:27
  • Yes, that's what I'm suggesting. – Radiodef Aug 16 '18 at 15:07
5
import java.util.*;

public class check { 
   int ans;
   String str="7 + 5";
   StringTokenizer st=new StringTokenizer(str);

   int v1=Integer.parseInt(st.nextToken());
   String op=st.nextToken();
   int v2=Integer.parseInt(st.nextToken());

   if(op.equals("+")) { ans= v1 + v2; }
   if(op.equals("-")) { ans= v1 - v2; }
   //.........
}
computingfreak
  • 4,939
  • 1
  • 34
  • 51
stone
  • 99
  • 1
  • 2
4

You might have a look at the Symja framework:

ExprEvaluator util = new ExprEvaluator(); 
IExpr result = util.evaluate("10-40");
System.out.println(result.toString()); // -> "-30" 

Take note that definitively more complex expressions can be evaluated:

// D(...) gives the derivative of the function Sin(x)*Cos(x)
IAST function = D(Times(Sin(x), Cos(x)), x);
IExpr result = util.evaluate(function);
// print: Cos(x)^2-Sin(x)^2
Laurent Magnin
  • 369
  • 3
  • 4
4

I think what ever way you do this it's going to involve a lot of conditional statements. But for single operations like in your examples you could limit it to 4 if statements with something like

String math = "1+4";

if (math.split("+").length == 2) {
    //do calculation
} else if (math.split("-").length == 2) {
    //do calculation
} ...

It gets a whole lot more complicated when you want to deal with multiple operations like "4+5*6".

If you are trying to build a calculator then I'd surgest passing each section of the calculation separatly (each number or operator) rather than as a single string.

BruteForce
  • 191
  • 4
  • 2
    It gets a whole lot more complicated as soon as you have to deal with multiple operations, operator precedence, parentheses, ... in fact anything that characterizes a real arithmetic expression. You cannot get there starting from this technique. – user207421 Jun 16 '16 at 03:52
4
package ExpressionCalculator.expressioncalculator;

import java.text.DecimalFormat;
import java.util.Scanner;

public class ExpressionCalculator {

private static String addSpaces(String exp){

    //Add space padding to operands.
    //https://regex101.com/r/sJ9gM7/73
    exp = exp.replaceAll("(?<=[0-9()])[\\/]", " / ");
    exp = exp.replaceAll("(?<=[0-9()])[\\^]", " ^ ");
    exp = exp.replaceAll("(?<=[0-9()])[\\*]", " * ");
    exp = exp.replaceAll("(?<=[0-9()])[+]", " + "); 
    exp = exp.replaceAll("(?<=[0-9()])[-]", " - ");

    //Keep replacing double spaces with single spaces until your string is properly formatted
    /*while(exp.indexOf("  ") != -1){
        exp = exp.replace("  ", " ");
     }*/
    exp = exp.replaceAll(" {2,}", " ");

       return exp;
}

public static Double evaluate(String expr){

    DecimalFormat df = new DecimalFormat("#.####");

    //Format the expression properly before performing operations
    String expression = addSpaces(expr);

    try {
        //We will evaluate using rule BDMAS, i.e. brackets, division, power, multiplication, addition and
        //subtraction will be processed in following order
        int indexClose = expression.indexOf(")");
        int indexOpen = -1;
        if (indexClose != -1) {
            String substring = expression.substring(0, indexClose);
            indexOpen = substring.lastIndexOf("(");
            substring = substring.substring(indexOpen + 1).trim();
            if(indexOpen != -1 && indexClose != -1) {
                Double result = evaluate(substring);
                expression = expression.substring(0, indexOpen).trim() + " " + result + " " + expression.substring(indexClose + 1).trim();
                return evaluate(expression.trim());
            }
        }

        String operation = "";
        if(expression.indexOf(" / ") != -1){
            operation = "/";
        }else if(expression.indexOf(" ^ ") != -1){
            operation = "^";
        } else if(expression.indexOf(" * ") != -1){
            operation = "*";
        } else if(expression.indexOf(" + ") != -1){
            operation = "+";
        } else if(expression.indexOf(" - ") != -1){ //Avoid negative numbers
            operation = "-";
        } else{
            return Double.parseDouble(expression);
        }

        int index = expression.indexOf(operation);
        if(index != -1){
            indexOpen = expression.lastIndexOf(" ", index - 2);
            indexOpen = (indexOpen == -1)?0:indexOpen;
            indexClose = expression.indexOf(" ", index + 2);
            indexClose = (indexClose == -1)?expression.length():indexClose;
            if(indexOpen != -1 && indexClose != -1) {
                Double lhs = Double.parseDouble(expression.substring(indexOpen, index));
                Double rhs = Double.parseDouble(expression.substring(index + 2, indexClose));
                Double result = null;
                switch (operation){
                    case "/":
                        //Prevent divide by 0 exception.
                        if(rhs == 0){
                            return null;
                        }
                        result = lhs / rhs;
                        break;
                    case "^":
                        result = Math.pow(lhs, rhs);
                        break;
                    case "*":
                        result = lhs * rhs;
                        break;
                    case "-":
                        result = lhs - rhs;
                        break;
                    case "+":
                        result = lhs + rhs;
                        break;
                    default:
                        break;
                }
                if(indexClose == expression.length()){
                    expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose);
                }else{
                    expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose + 1);
                }
                return Double.valueOf(df.format(evaluate(expression.trim())));
            }
        }
    }catch(Exception exp){
        exp.printStackTrace();
    }
    return 0.0;
}

public static void main(String args[]){

    Scanner scanner = new Scanner(System.in);
    System.out.print("Enter an Mathematical Expression to Evaluate: ");
    String input = scanner.nextLine();
    System.out.println(evaluate(input));
}

}

Farvardin
  • 5,336
  • 5
  • 33
  • 54
chejaras
  • 862
  • 5
  • 10
4

A Java class that can evaluate mathematical expressions:

package test;

public class Calculator {

    public static Double calculate(String expression){
        if (expression == null || expression.length() == 0) {
            return null;
        }
        return calc(expression.replace(" ", ""));
    }
    public static Double calc(String expression) {
        String[] containerArr = new String[]{expression};
        double leftVal = getNextOperand(containerArr);
        expression = containerArr[0];
        if (expression.length() == 0) {
            return leftVal;
        }
        char operator = expression.charAt(0);
        expression = expression.substring(1);

        while (operator == '*' || operator == '/') {
            containerArr[0] = expression;
            double rightVal = getNextOperand(containerArr);
            expression = containerArr[0];
            if (operator == '*') {
                leftVal = leftVal * rightVal;
            } else {
                leftVal = leftVal / rightVal;
            }
            if (expression.length() > 0) {
                operator = expression.charAt(0);
                expression = expression.substring(1);
            } else {
                return leftVal;
            }
        }
        if (operator == '+') {
            return leftVal + calc(expression);
        } else {
            return leftVal - calc(expression);
        }

    }
    
    private static double getNextOperand(String[] exp){
        double res;
        if (exp[0].startsWith("(")) {
            int open = 1;
            int i = 1;
            while (open != 0) {
                if (exp[0].charAt(i) == '(') {
                    open++;
                } else if (exp[0].charAt(i) == ')') {
                    open--;
                }
                i++;
            }
            res = calc(exp[0].substring(1, i - 1));
            exp[0] = exp[0].substring(i);
        } else {
            int i = 1;
            if (exp[0].charAt(0) == '-') {
                i++;
            }
            while (exp[0].length() > i && isNumber((int) exp[0].charAt(i))) {
                i++;
            }
            res = Double.parseDouble(exp[0].substring(0, i));
            exp[0] = exp[0].substring(i);
        }
        return res;
    }


    private static boolean isNumber(int c) {
        int zero = (int) '0';
        int nine = (int) '9';
        return (c >= zero && c <= nine) || c =='.';
    }

    public static void main(String[] args) {
        System.out.println(calculate("(((( -6 )))) * 9 * -1"));
        System.out.println(calc("(-5.2+-5*-5*((5/4+2)))"));

    }

}
Efi G
  • 937
  • 8
  • 15
  • 3
    Doesn't handle operator precedence correctly. There are standard ways of doing this, and this isn't one of them. – user207421 Apr 17 '18 at 02:08
  • EJP, can you please point where there is a problem with operator precedence? i fully agree on the fact that it is not the standard way to do it. the standard ways were already mentioned in previous posts, the idea was to show another way to do it. – Efi G Apr 22 '18 at 07:30
  • I evaluated 16+10×500−3,000÷50 in calculator as well and the answer should be 4956 . Your algorithm prints -414 – deepakl.2000 May 23 '21 at 11:57
  • 1
    not sure comma is handled. the div operator is '/' and not '÷' – Efi G May 24 '21 at 05:18
  • Yes it's working now,can you help me with a similar issue at https://stackoverflow.com/questions/6666169/javaevaluation-of-mathematical-expression-only-from-left-to-right – deepakl.2000 Sep 25 '21 at 18:42
  • @Efi G need help for below as well https://stackoverflow.com/questions/6666169/javaevaluation-of-mathematical-expression-only-from-left-to-right – deepakl.2000 Sep 27 '21 at 15:27
  • @EfiG great implementation. there is a bug, the following expression raises an error: "(1+3) * (4+5)". to fix that, just remove the condition "if (expression.startsWith("(") && expression.endsWith(")")) { ... }" – Jonathan May 01 '22 at 09:43
  • Thanks @Jonathan, edited as you suggested – Efi G May 01 '22 at 16:42
2

How about something like this:

String st = "10+3";
int result;
for(int i=0;i<st.length();i++)
{
  if(st.charAt(i)=='+')
  {
    result=Integer.parseInt(st.substring(0, i))+Integer.parseInt(st.substring(i+1, st.length()));
    System.out.print(result);
  }         
}

and do the similar thing for every other mathematical operator accordingly ..

keyser
  • 18,829
  • 16
  • 59
  • 101
konxie
  • 113
  • 2
  • 7
  • 9
    You should read about writing efficient math expression parsers. There is a computer science methodology to it. Take a look at ANTLR, for example. If you think well about what you wrote you'll see that things like (a+b/-c)*(e/f) will not work with your idea or the code will be super duper dirty and inefficient. – Daniel Nuriyev Apr 24 '14 at 18:03
2

It is possible to convert any expression string in infix notation to a postfix notation using Djikstra's shunting-yard algorithm. The result of the algorithm can then serve as input to the postfix algorithm, which returns the result of the expression.

Emmanuel John
  • 2,296
  • 1
  • 23
  • 30
2

Yet another option: https://github.com/stefanhaustein/expressionparser

I have implemented this to have a simple but flexible option to permit both:

The TreeBuilder linked above is part of a CAS demo package that does symbolic derivation. There is also a BASIC interpreter example and I have started to build a TypeScript interpreter using it.

Stefan Haustein
  • 18,427
  • 3
  • 36
  • 51
2

External library like RHINO or NASHORN can be used to run javascript. And javascript can evaluate simple formula without parcing the string. No performance impact as well if code is written well. Below is an example with RHINO -

public class RhinoApp {
    private String simpleAdd = "(12+13+2-2)*2+(12+13+2-2)*2";

public void runJavaScript() {
    Context jsCx = Context.enter();
    Context.getCurrentContext().setOptimizationLevel(-1);
    ScriptableObject scope = jsCx.initStandardObjects();
    Object result = jsCx.evaluateString(scope, simpleAdd , "formula", 0, null);
    Context.exit();
    System.out.println(result);
}
Manish
  • 1,452
  • 15
  • 25
0
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class test2 {
    public static void main(String[] args) throws ScriptException {
        String s = "10+2";
        ScriptEngineManager mn = new ScriptEngineManager();
        ScriptEngine en = mn.getEngineByName("js");
        Object result = en.eval(s);
        System.out.println(result);
    }
}
Md Adilur Rashid
  • 700
  • 7
  • 13
  • I am getting the following warning when I use this code: nashorn engine is planned to be removed from a future jdk release – h.Ebrahimi Feb 10 '23 at 14:13
0

I have done using iterative parsing and shunting Yard algorithm and i have really enjoyed developing the expression evaluator ,you can find all the code here

https://github.com/nagaraj200788/JavaExpressionEvaluator

Has 73 test cases and even works for Bigintegers,Bigdecimals

supports all relational, arithmetic expression and also combination of both . even supports ternary operator .

Added enhancement to support signed numbers like -100+89 it was intresting, for details check TokenReader.isUnaryOperator() method and i have updated code in above Link

Nagappa L M
  • 1,452
  • 4
  • 20
  • 33
0

If anybody needs precise decimal numbers I made a modification which uses BigDecimal instead of double.

I got inspired by Boann's answer and modified it to use BigDecimal.

public static BigDecimal eval(final String str, MathContext mathContext) {
    if (ObjectUtils.isEmpty(str)) {
        return null;
    }

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

        BigDecimal parse() {
            nextChar();
            BigDecimal 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

        BigDecimal parseExpression() {
            BigDecimal x = parseTerm();
            for (;;) {
                if      (eat('+')) x = x.add(parseTerm()); // addition
                else if (eat('-')) x = x.subtract(parseTerm()); // subtraction
                else return x;
            }
        }

        BigDecimal parseTerm() {
            BigDecimal x = parseFactor();
            for (;;) {
                if      (eat('*')) x = x.multiply(parseFactor()); // multiplication
                else if (eat('/')) x = x.divide(parseFactor(), mathContext); // division
                else return x;
            }
        }

        BigDecimal parseFactor() {
            if (eat('+')) return parseFactor(); // unary plus
            if (eat('-')) return parseFactor().negate(); // unary minus

            BigDecimal 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 = new BigDecimal(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 ("sqrt".equals(func)) {
                    x = x.sqrt(mathContext);
                } else {
                    throw new RuntimeException("Unknown function: " + func);
                }
            } else {
                throw new RuntimeException("Unexpected: " + (char)ch);
            }

            if (eat('^')) x = x.pow(parseFactor().intValue(), mathContext); // exponentiation

            return x;
        }
    }.parse();
}

Use it in a following way:

// set precision and rounding mode
MathContext mathContext = new MathContext(10, RoundingMode.HALF_UP);
BigDecimal result = YourLib.eval("33+33", mathContext);
michal.jakubeczy
  • 8,221
  • 1
  • 59
  • 63