Giter Site home page Giter Site logo

ezylang / evalex Goto Github PK

View Code? Open in Web Editor NEW
962.0 42.0 261.0 815 KB

EvalEx is a handy expression evaluator for Java, that allows to evaluate simple mathematical and boolean expressions.

Home Page: https://ezylang.github.io/EvalEx/

License: Apache License 2.0

Java 100.00%
expression-evaluator expression-parser java

evalex's Issues

Combine IF ?

Hi, I'm looking to use your lib but I have a question: can we combine IF statement like

IF(cond1,IF(cond2,true,false),IF(cond3,true,false))

Thanx

Scientific notation

Scientific notation ("2.2E-16") within expressions currently is not supported.

Example (calling EvalEx from BeanShell) ...

addClassPath("drawj2d.jar"); // BeanShell (bsh2) syntax
import com.udojava.evalex.*;
import java.math.BigDecimal;

String expr2 = "2.2E-16 * 10.2";
Expression expression = new Expression(expr2);
BigDecimal result = expression.eval();

... fails throwing an exception:

java.lang.RuntimeException: Unknown operator or function: E
        at com.udojava.evalex.Expression.shuntingYard(Expression.java:1149)
        at com.udojava.evalex.Expression.getRPN(Expression.java:1353)
        at com.udojava.evalex.Expression.eval(Expression.java:1166)

I once asked in the comments section of Udo's blog. The answer was:

Scientific notation, this could get tricky. But I guess it is possible to implement in the tokenizer.

The java method call new BigDecimal("2.2E-16") does parse numbers in scientific notation. So it seems that it would be enough to tell the EvalEx tokenizer to understand the number in scientific notation as a single statement.
Trying ...

Index: com/udojava/evalex/Expression.java
===================================================================
--- com/udojava/evalex/Expression.java  (revision 263)
+++ com/udojava/evalex/Expression.java  (working copy)
@@ -593,7 +593,8 @@
                                ch = input.charAt(++pos);
                        }
                        if (Character.isDigit(ch)) {
-                               while ((Character.isDigit(ch) || ch == decimalSeparator)
+                               while ((Character.isDigit(ch) || ch == decimalSeparator
+                                                || ch == 'e' || ch == 'E')
                                                && (pos < input.length())) {
                                        token.append(input.charAt(pos++));
                                        ch = pos == input.length() ? 0 : input.charAt(pos);
@@ -1056,7 +1057,8 @@
                        return false;
                for (char ch : st.toCharArray()) {
                        if (!Character.isDigit(ch) && ch != minusSign
-                                       && ch != decimalSeparator)
+                                       && ch != decimalSeparator
+                                        && ch != 'e' && ch != 'E')
                                return false;
                }
                return true;
@@ -1398,6 +1400,7 @@
                        }
                        counter++;
                }
+                System.err.println("DEBUG: " + rpn.toString());
                if (counter > 1) {
                        throw new ExpressionException("Too many numbers or variables");
                } else if (counter < 1) {

... works for expressions like "2.2E16 * 10.2" but still fails for "2.2E-16".

Any idea how EvalEx can be told to recognize "2.2E-16" as a single number?

support variable names starting with '_'

It would be nice for EvalEx to support variable names starting with '_'

I tested it simply by changing that line with this:

            } else if (Character.isLetter(ch) || (ch == '_')) {

I also changed that line to this:

                while (!Character.isLetter(ch) && !Character.isDigit(ch) && ch != '_'

slow string replace

here you use String.replace with regex.

First question is: is the regex really necessary? I couldn't face a case where it was necessary.
Maybe be you could make it an option? I use your lib for realtime computation and so every little improvement is a big improvement ;)

Now if you decided to go to a simple replace like i did then you would face the problem that String.replace always uses regex and is really slow.

For reference you can use a faster replace like this:

public static String fastReplace(String source, String os, String ns) {
        if (source == null) {
            return null;
        }
        int i = 0;
        if ((i = source.indexOf(os, i)) >= 0) {
            char[] sourceArray = source.toCharArray();
            char[] nsArray = ns.toCharArray();
            int oLength = os.length();
            StringBuilder buf = new StringBuilder(sourceArray.length);
            buf.append(sourceArray, 0, i).append(nsArray);
            i += oLength;
            int j = i;
            // Replace all remaining instances of oldString with newString.
            while ((i = source.indexOf(os, i)) > 0) {
                buf.append(sourceArray, j, i - j).append(nsArray);
                i += oLength;
                j = i;
            }
            buf.append(sourceArray, j, sourceArray.length - j);
            source = buf.toString();
            buf.setLength(0);
        }
        return source;
    }

Default precision and scale

Hello, I found EvalEx is very useful and I have similar needs of BigDecimal in expression evaluator.
I really appreciate your great work.

Anyway, I found several issues when I tried to use it.

The first one is that Expression seems have an unclear default precision.
I just use this code to test:

Expression exp = new Expression(str);
System.out.println(exp.eval());

The input and output will be:
1.1234895 => 1.12349
1.1234891 => 1.123489

What I expected is:
1.1234895 => 1.1234895
1.1234891 => 1.1234891

I can get what I expected by adding:

exp.setPrecision(0);

It can solve this problem but I just get confused by the default behavior.
Can exp.setPrecision(0); be a default condition?

Another issue is that I could not control the scale during calculation.
We can control precision by setPrecision and ROUND(expression,precision) but there is no function for scale.

How do you think about these?
I'll dig into the code and try to contribute if I have time.

It is very nice of you if you could do some improvement.

Thank you very much!

IF evaluation

Hi,

why does IF(TRUE,1,1/0) throw a division by zero error ? Seems like 1/0 is evaluated regardless of
the result of the if condition ?

thanks!
gerald

IF-Expression (both cases are executed)

Hello Udo,
thanks for your project.
I have a problem with the IF-Expression. If a condition evaluates to false both expressions are executed.

example:
value1 = 0;
value2 = 2;
IF(value1>0,value2/value1,0)

value2/value1 (2/0) will be executed.
In this case Java throws an "ArithmeticException: Division by zero".

request:
First the condition should be tested and then one of the two values should be excuted.

Can you fix that problem?

I am looking forward to hearing from you.

Best regards,
Kai

BigDecimal Rounding problem

Here is my code

public class test {
public static void main(String args[])
{
String str="(1/3)*3";
Expression exp = new Expression(str);
System.out.println("ans: "+exp.eval().toPlainString());
}
}

The output should be 1 but i am getting something lke this ans: 0.9999999.

I read on internet that toPlainString() removes such problem but it is not working in my case. Can you suggest me what to do.

Sorry to disturb you again

Mutability of Expression makes it non ideal for using the cached instance

public class Equation {

    private String expressionString;
    private Expression expression;

    public Equation(String expressionString) {
        hasLength(expressionString, "Invalid Expression %s specified");
        this.expressionString = expressionString;
        expression = new Expression(expressionString);
    }
}

the above equation instance has expression string and constructs Expression once and caches the equation, but since the Expression mutates the internal map, it becomes very cumbersome to evaluate the expression as the below

(-327865.5593 + Yearly * 7699.1161 + FQ1 * 118965.3548 + FQ2 * -19003.9418 -38640 + FQ3 * 75560 + FQ4 * -21700)/1000000

where we can expect only one fQ per execution, and while doing batch execution, the evaluation can quickly get cumbersome,

what I basically want to do is

public List<BigDecimal> eval(Map<TimeLineInQuarter.Quarters, Double> variables, Yearly y) {
              Arrays.stream(TimeLineInQuarter.Quarters.values()).forEach(
                Errors.rethrow().wrap(q -> {
                            expression.with("Yearly", new BigDecimal(y.quarterly(q)))
                                    .with(q.name(), new BigDecimal(variables.get(q)));
                        }
                )
        );

      //not intended to compile, Idea is I will be able to execute the equation for every quarter for a given year
        return collectedvalue;

    }

Making the Expression immutable, I would be able to use the same Expression multiple times, but in the above case, I will end up constructing one expression per execution, though it may appear to be specific to my equation, I would stress that Immutable Expression would be ideal as construction of Expression as I see is costly

setRoundingScale?

Hallo,

ich habe ein Problem mit MathContext der Expression-Klasse:
Die Precision des MathContext bezieht sich auf alle Ziffern inklusive der Vorkommastellen, sodass meine Ergebnisse gegebenenfalls sehr ungenau werden (bei MathContext.DECIMAL32):
Könnte man vielleicht statt der Precision einen Scale setzen, sodass diese sich nur auf die Nachkommestellen beziehen? (setPrecision vs setScale?)

Ich glaube ich habe noch nicht verstanden, warum überhaupt mit einem MathContext gearbeitet wird (anstelle eines Scales mit RoundingMode), können Sie mir da weiterhelfen?

How i can turn off the scientific notation for results?

Hi, first i have to say my congratulations for your project, very god job man. I am mading a simple calculator (my first Android app), and i have issues with the notation of results:
For example, if i do "10+10" instead of give me "20" as result it give me "2E+1".

You know how i cant fix it?

PD: sorry for my bad english

Evaluation performance for large numbers

Hey Udo, first of all thanks for the awesome library!

I'm using the EvalEx library for the Liferay portal project but we've found something that impacts the performance of some operations.

For example expression such as SQRT(9123744984893^993837) takes a lot of time to evaluate and the eval() method hangs for a while.

We can solve this at our side, but I was wondering if that's something you would like to include in your library. Something like a timed out condition for evaluations. I can send you a pull request if you think so.

Let me know what you think.

Best Regards @uklimaschewski,

dynamic precision

Hay Udo. Thank you very much for this amazing repository. This is exactly what I was looking for.

This is not a bug report, but rather a personal problem I ran into. Would be great if you could maybe help me out.

I am trying to get the right precision for every occasion:

  1. A normal decimal number should have a a precision of 5
    (1/3 = 1.33333)
  2. A calculation with decimal numbers should orient on the largest decimal point and set the precision according to it:
    (0.1234567 + 0.1234 = 0.2468567)
  3. Big numbers should have the maximum precision:
    (12345678 * 123456 = 1524148023168)

I have tried several things like setting the precision to 128, which gives me (3) and (2) but (1) spammed the output with too many periodic "3"s.

Could you maybe hint me in the right direction?

Thanks again for the amazing code
Liebe Grüsse Haeri

other function use

hello;

my function

public int test(int a, int b, int c) {
return a+b+c;;
}

can i use

Expression e = new Expression("2 * test(12,4,8)");

        e.addFunction(e.new Function("test", -1) {
            @Override
            public BigDecimal eval(List<BigDecimal> parameters) {
                if (parameters.size() == 0) {
                    throw new Expression.ExpressionException("average requires at least one parameter");
                }
                BigDecimal avg = new BigDecimal(0);
                for (BigDecimal parameter : parameters) {
                    avg = avg.add(parameter);
                }
                return avg.divide(new BigDecimal(parameters.size()));
            }
        });

        e.eval(); 

thank you, and thank you google translate :)

Support for implicit multiplication

Hello,
are there any plans to support implicit multiplications, so that 5SIN(2) will be interpreted as 5*SIN(2)? (Or is this even already possible and I am just unable to find this functionality?)

Edit: I tried to solve this issue by modifing the shuntingYard method by adding addition if-statements like this

if (isNumber(token)) {
      if (previousToken != null && ")".equals(previousToken))
      {
           stack.push("×");
      }
     ...
} else if (functions.containsKey(token.toUpperCase(Locale.ROOT))) {
     if (previousToken != null && isNumber(previousToken))
     {
            stack.push("×");
     }
 ....

This seems to work after a few test, but I am not completely sure about it. Are there any opinions?

Max function for more than 2 arguments

max(3.0, 2.0, 1.0) returns 2.0. The expected answer would be 3.0.
min(1.0, 2.0, 3.0) returns 2.0. The expected answer would be 1.0.

Currently max expects two arguments. If more arguments are passed to the function, the last two are taken (no error). This is not obvious.

Could max (and min) be extended to accept any number of arguments (>1)?

support for decimals without leading zeros

in Expression.java, line 595, I had to change the line to:

if (Character.isDigit(ch) || (ch == decimalSeparator && Character.isDigit(peekNextChar()))) {

in order to account for .5 instead of 0.5

Support for more trigonometric functions

currently it supports Sin, Cos, Tan. hyperbolic and also arc.
it would be nice if it could answer below functions:
Sec, Csc
Sech, Csch
Cot, aCot, Coth
aSinh, aCosh, aTanh

minus "-" doesn't work before Open Parentheses "("

first, you did a great job. your EvalEx is awsome. but there is one annoying thing. - doesn't work before (. below expressions are really common and used alot, but evalex can't handle it. and throws exception.
(-(5))
(-(5+3)+3)
that would be great if it could handle above expressions.

Bug

Hallo Udo,

die if() Formel hat einen kleinen Bug.
Wenn man if (true, -x+y,0) auswerten, möchte, dann kommt es zu einer Fehlermeldung.

Quick Question about BigDecimal and If function

The project I am using EvalEx on is money based therefore all calculation falls in the limits of double. BigDecimal arithmetic function is known to be much more slower than primitive like double. I am exploring the possibility of wrapping the Values EvalEX in a pseudo class that supports BigDecimal as well as primitives like double or even floats in extreme case. For use cases that are can tolerate precision losses based on performance gain.

This is going to dramatically change the code so i am asking to see if you will be willing to accept such a pull request.

If i go ahead with this modification. I would like to improve the if function as well. Consider the following
if(a =0, 0 , 12/a)
The above does not work well because 12/a is evaluated regardless of the outcome of a=0. This was a problem i ran into. I solved it by returning 0 for any divisible by 0 in my specific context. This is not ideal. I would like to lazily evaluate the parameters of the if based on the outcome of the boolean tester.

Scientific Notation Support

Hello, I have been trying to use the lib for large numbers and the result is shown in scientific notation, which is great. Anyway there's an issue when I try to use that result for a new expression, lets say:
2.551633e11*5

I get an exception:
java.lang.RuntimeException: Unknown operator or function: e11

And I've checked through the github repository and I've found some unit tests for scientific notation, but I couldnt find this in the lib. Is this supported in future versions and not in 1.0?

Thanks in advance.

ASIN, ACOS and ATAN

Also, I think it is a good idea to add this line to all trigo functions:

if (d == 0) return BigDecimal.ZERO;

Default precision is 7 instead of 13 ?

I found out that the precision of BigDecimal result from Expression.eval() is 7 instead of 13 which is default in BigDecimal:

   BigDecimal bd = new BigDecimal("1442503274032");
   System.out.println(bd.precision());    // print 13

   Expression expr = new Expression("1442503274032");
   BigDecimal res = expr.eval();
   System.out.println(res.precision());    // print 7

Is there a reason why use 7 instead of 13 ?
I know the precision can be set but I prefer if it conforms to the default.

Feature Request: NULL als Konstante

Hallo Herr Klimaschewski,
könnten Sie mir helfen,
evalEx so zu erweitern, dass man eine Konstante mit null hat.

Brauchen tue ich z.B. für eine IF-ELSE-Formel:

    @Test
    public void testNullAsConstant()
    {
        String formel = "if(y==0, NULL, x/y)";
        Expression e = new Expression(formel);
        e.with("y", BigDecimal.ZERO);
        e.with("x", BigDecimal.TEN);
        e.with("NULL", (BigDecimal) null);

        // ------ VERIFY ------- //
        assertNull(e.eval());
    }

ich dachte man könnte mit Hinzufügen einer neuen "Variable" das Problem lösen:
m_variables.put("NULL", null);

Aber da bekomme ich auch ein NPE.

Vielen Dank im Voraus.

Support of nested expressions?

I've looked at the code quickly and tried to figure out a quick way to patch it to support nested expressions, but have been unable to do it easily.

I'm looking to support the following use case:

var1 = 10;
var2 = "5 * var1";
var3 = "25 + var2";

Expression e = new Expression( var3 ).with( "var2", var2).with( "var1", var1);
e.eval();

Is this functionality easily accomplished/added?

Functions use.

hi;

//my functions;

public Double FICLC(String item, Integer param){
return 1d;
}
public Double FGCLC(String item, String itemtwo, Integer param){
return 1d;
}
public Double FYCLC(String item){
return 1d;
}

new Expression("(FICLC("012844098501",1)>=1) && (FGCLC("A11","ABC",1)>1) && (FYCLC("A11")=1) ");

I have to watch how a road ?

Thank you.

factorial operator

insert the following in the operators list:

    ///////////////////////////////////////////////////
    addOperator(new Operator("!", 50, true)
    {
        BigDecimal fac(BigDecimal n, BigDecimal acc)
        {
            if (n.equals(BigDecimal.ONE))
            {
                return acc;
            }
            BigDecimal lessOne = n.subtract(BigDecimal.ONE);
            return fac(lessOne, acc.multiply(lessOne, mc));
        }

        @Override
        public BigDecimal eval (BigDecimal v1, BigDecimal v2)
        {
            return fac (v1, v1);
        }
    });
    ///////////////////////////////////////////////////

... then replace every ! in the input string with !0 to fake a second op
now one can calculate
3!^4 = 1296
3! = 6
8! = 40320
and so on ...

Maven Release?

Hi Udo,

This project looks great, thanks for your effort! We'd like to use EvalEx in one of our projects but see it hasn't been released to Maven central. Would you be able to release this?

The only alternative for us would be to download the source, build it, upload it to our own repository and work off of that. But that sort of defeats the dependency management feature of Maven and we'd opt to use a different tool.

I'd be happy to help you release this if you're new to the process.

Best,
Sean

Make Function and Operator static

If made static, I wouldn't need to create a Function dependent of the variable, the same way you do inside Expression class.

There should be no other changes to the code Also, I could use it in Kotlin ;D

use String parameters in custom functions

Hi dear
I want to evaluate a function like this: myFunction(23A12B,H341B1), but the parser didn't recongize the whole 23A12B as a parameter!
how do i can do this?

Problem with sqrt

Hello Udo,
Thank you producing such a useful library.
I came across an issue where sqrt function is getting stuck in this loop.
do {
ixPrev = ix;
ix = ix.add(n.divide(ix)).shiftRight(1);
// Give other threads a chance to work;
Thread.yield();
} while (ix.compareTo(ixPrev) != 0);

I ended up overriding the function with java's sqrtj implementation.

Best way to retrieve variables from Expressions?

Hi Udo,

is there any convinient way to retrieve all variables which are used within an Expression? Something like that?
Expression exp = new Expression("$variableA - $variableB * 10)
exp.getVariables(); // returns variableA and variableB in List<String>

Problem with brackets

There are two issue that i found and i dont know how to fix it. Issue is if you have string like this "2_3(5_3)", the answer should be 90 but the program gives 45. Any suggest about how shall i fix this??

Also expression like "2_3_(5*3)" should give answer 90 but instead it gives 9E+1.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.