Skip to content

Latest commit

 

History

History
413 lines (340 loc) · 17.8 KB

README.md

File metadata and controls

413 lines (340 loc) · 17.8 KB

EvalEx - Java Expression Evaluator

Build Status Quality Gate Status

Introduction

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

Key Features:

  • Uses BigDecimal for calculation and result
  • No dependencies to external libraries
  • Precision and rounding mode can be set
  • Supports variables
  • Standard boolean and mathematical operators
  • Standard basic mathematical and boolean functions
  • Custom functions and operators can be added at runtime
  • Functions can be defined with a variable number of arguments (see MIN and MAX functions)
  • Supports for hexadecimal numbers and scientific notations of numbers
  • Supports string literals in functions
  • Supports implicit multiplication, e.g. (a+b)(a-b) or 2(x-y) which equals to (a+b)*(a-b) or 2*( x-y)

Download / Maven

You can download the binaries, source code and JavaDoc jars from Maven Central .

The project and source code in zip and tar.gz format can also be downloaded from the projects release area.

To include it in your Maven project, refer to the artifact in your pom. For example:

<dependencies>
    <dependency>
        <groupId>com.udojava</groupId>
        <artifactId>EvalEx</artifactId>
        <!-- change to desired version -->
        <version>2.6</version>
    </dependency>
</dependencies>

If you're using gradle add to your project's app build.gradle:

dependencies {
    ...
    compile 'com.udojava:EvalEx:2.6'
}

FAQ

A list of frequently asked questions (and answers) can be found here: FAQ

Usage Examples

 BigDecimal result = null;
 
 // Simple usage with an expression without variables.
 Expression expression = new Expression("1+1/3");
 result = expression.eval(); // 1.333333
 // Lowering the precision.
 expression.setPrecision(2);
 result = expression.eval(); // 1.3
 
 // A more complex expression showing support for unary operators.
 result = new Expression("(3.4 + -4.1)/2").eval(); // -0.35
 
 // Using functions and variables.
 result = new Expression("SQRT(a^2 + b^2)")
         .with("a", "2.4")
         .and("b", "9.253")
         .eval(); // 9.5591845
 
 // Using pre-created BigDecimals for variables
 BigDecimal a = new BigDecimal("2.4");
 BigDecimal b = new BigDecimal("9.235");
 result = new Expression("SQRT(a^2 + b^2)")
         .with("a", a)
         .and("b", b)
         .eval(); // 9.5591845
 
 // Increasing the precision and setting a different rounding mode.
 result = new Expression("2.4/PI")
         .setPrecision(128)
         .setRoundingMode(RoundingMode.UP)
         .eval(); // 0.763943726841...
 
 // Using a function to receive a random number and test it.
 result = new Expression("random() > 0.5").eval(); // 1

 // Using more functions and showing the boolean support.
 result = new Expression("not(x<7 || sqrt(max(x,9,3,min(4,3))) <= 3)")
         .with("x", "22.9")
         .eval(); // 1
 
 // Calling a pre-defined function.
 result = new Expression("log10(100)").eval(); // 2

Precision

The default precision is set to 7 digits (MathContext.DECIMAL32). Depending on your use-case you will want to set a different precision to get accurate results:

 new Expression("1/3")
         .setPrecision(3)
         .eval(); // 0.333

 new Expression("1/3")
         .setPrecision(12)
         .eval(); // 0.333333333333

If you do not increase the precision as needed, you will get inaccurate results:

 new Expression("123456789 + 123456789").eval(); // 246913600

 new Expression("123456789 + 123456789")
         .setPrecision(12)
         .eval(); // 246913578

Default Settings

The default settings for an expression can be set on creation through an ExpressionSettings object. It can be created using a builder pattern:

ExpressionSettings settings = ExpressionSettings.builder()
         .mathContext(MathContext.DECIMAL128)
         .powerOperatorPrecedenceHigher()
         .build();
new Expression("-2^2", settings).eval();

Supported Operators

Mathematical Operators
OperatorDescription
+Additive operator / Unary plus
-Subtraction operator / Unary minus
*Multiplication operator, can be omitted in front of an open bracket
/Division operator
%Remainder operator (Modulo)
^Power operator
Boolean Operators*
OperatorDescription
=Equals
==Equals
!=Not equals
<>Not equals
<Less than
<=Less than or equal to
>Greater than
>=Greater than or equal to
&&Boolean and
||Boolean or
*Boolean operators result always in a BigDecimal value of 1 or 0 (zero). Any non-zero value is treated as a _true_ value. Boolean _not_ is implemented by a function.

Supported Functions

Function*Description
NOT(expression)Boolean negation, 1 (means true) if the expression is not zero
IF(condition,value_if_true,value_if_false)Returns one value if the condition evaluates to true or the other if it evaluates to false
RANDOM()Produces a random number between 0 and 1
MIN(e1,e2, ...)Returns the smallest of the given expressions
MAX(e1,e2, ...)Returns the biggest of the given expressions
ABS(expression)Returns the absolute (non-negative) value of the expression
ROUND(expression,precision)Rounds a value to a certain number of digits, uses the current rounding mode
FLOOR(expression)Rounds the value down to the nearest integer
CEILING(expression)Rounds the value up to the nearest integer
LOG(expression)Returns the natural logarithm (base e) of an expression
LOG10(expression)Returns the common logarithm (base 10) of an expression
SQRT(expression)Returns the square root of an expression
SINR(expression)Returns the trigonometric sine of an angle (in radians)
COSR(expression)Returns the trigonometric cosine of an angle (in radians)
TANR(expression)Returns the trigonometric tangensuiju of an angle (in radians)
COTR(expression)Returns the trigonometric cotangens of an angle (in radians)
SECR(expression)Returns the secant (in radians)
CSCR(expression)Returns the cosecant (in radians)
ASINR(expression)Returns the angle of asin (in radians)
ACOSR(expression)Returns the angle of acos (in radians)
ATANR(expression)Returns the angle of atan (in radians)
ACOTR(expression)Returns the angle of acot (in radians)
ATAN2R(y,x)Returns the angle of atan2 (in radians)
SIN(expression)Returns the trigonometric sine of an angle (in degrees)
COS(expression)Returns the trigonometric cosine of an angle (in degrees)
TAN(expression)Returns the trigonometric tangens of an angle (in degrees)
COT(expression)Returns the trigonometric cotangens of an angle (in degrees)
SEC(expression)Returns the secant (in degrees)
CSC(expression)Returns the cosecant (in degrees)
ASIN(expression)Returns the angle of asin (in degrees)
ACOS(expression)Returns the angle of acos (in degrees)
ATAN(expression)Returns the angle of atan (in degrees)
ACOT(expression)Returns the angle of acot (in degrees)
ATAN2(y,x)Returns the angle of atan2 (in degrees)
SINH(expression)Returns the hyperbolic sine of a value
COSH(expression)Returns the hyperbolic cosine of a value
TANH(expression)Returns the hyperbolic tangens of a value
COTH(expression)Returns the hyperbolic cotangens of a value
SECH(expression)Returns the hyperbolic secant (in degrees)
CSCH(expression)Returns the hyperbolic cosecant (in degrees)
ASINH(expression)Returns the angle of hyperbolic sine (in degrees)
ACOSH(expression)Returns the angle of hyperbolic cosine (in degrees)
ATANH(expression)Returns the angle of hyperbolic tangens of a value
RAD(expression)Converts an angle measured in degrees to an approximately equivalent angle measured in radians
DEG(expression)Converts an angle measured in radians to an approximately equivalent angle measured in degrees
FACT(expression)Retuns the factorial value of an integer. Will return 1 for 0 or a negative number
*Functions names are case insensitive.

Supported Constants

 
ConstantDescription
eThe value of e, exact to 70 digits
PIThe value of PI, exact to 100 digits
TRUEThe value one
FALSEThe value zero
NULLThe null value

Add Custom Operators

Custom operators can be added easily, simply create an instance of Expression.Operator and add it to the expression. Parameters are the operator string, its precedence and if it is left associative. The operators eval() method will be called with the BigDecimal values of the operands. All existing operators can also be overridden.

For example, add an operator x >> n, that moves the decimal point of x n digits to the right:

Expression e = new Expression("2.1234 >> 2");

e.addOperator(new AbstractOperator(">>", 30, true) {
    @Override
    public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
        return v1.movePointRight(v2.toBigInteger().intValue());
    }
});

e.eval(); // returns 212.34

Or another example, add a postfix unary operator n!, that calculates the factorial of n. The parameters for postfix unary operators are the operator's string, its precedence, if it is left associative, is it is boolean and if it is unary (true).

Expression e = new Expression("4!");

e.addOperator(new AbstractOperator("!", Expression.OPERATOR_PRECEDENCE_POWER_HIGHER + 1, true, false, true) {
    @Override
    public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
        if(v1 == null) {
            throw new ArithmeticException("Operand may not be null");
        }
        if(v1.remainder(BigDecimal.ONE) != BigDecimal.ZERO) {
            throw new ArithmeticException("Operand must be an integer");
        }
        BigDecimal factorial = v1;
        v1 = v1.subtract(BigDecimal.ONE);
        if (factorial.compareTo(BigDecimal.ZERO) == 0 || factorial.compareTo(BigDecimal.ONE) == 0) {
            return BigDecimal.ONE;
        } else {
            while (v1.compareTo(BigDecimal.ONE) > 0) {
                factorial = factorial.multiply(v1);
                v1 = v1.subtract(BigDecimal.ONE);
            }
            return factorial;
        }
    }
});

e.eval(); // returns 24

Add Custom Functions

Adding custom functions is as easy as adding custom operators. Create an instance of Expression.Functionand add it to the expression. Parameters are the function name and the count of required parameters. The functions eval() method will be called with a list of the BigDecimal parameters. A -1 as the number of parameters denotes a variable number of arguments. All existing functions can also be overridden.

For example, add a function average(a,b,c), that will calculate the average value of a, b and c:

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

e.addFunction(new AbstractFunction("average", -1) {
    @Override
    public BigDecimal eval(List<BigDecimal> parameters) {
        if (parameters.size() == 0) {
            throw new 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(); // returns 16

Custom Functions With String Parameters

You can create a custom function with string parameters. Create an instance of Expression.LazyFunctionand add it to the expression. Parameters are the function name and the count of required parameters. The functions lazyEval() method will be called with a list of the LazyNumber parameters. A -1 as the number of parameters denotes a variable number of arguments. String parameters needs to be surrounded by ".

For example, add a function STREQ("string1","string2"), that will compare whether string1 and string2 are equal:

Expression e = new Expression("STREQ(\"test\", \"test2\")");
e.addLazyFunction(new AbstractLazyFunction("STREQ", 2) {
    private LazyNumber ZERO = new LazyNumber() {
        public BigDecimal eval() {
            return BigDecimal.ZERO;
        }
        public String getString() {
            return "0";
        }
    };
    private LazyNumber ONE = new LazyNumber() {
        public BigDecimal eval() {
            return BigDecimal.ONE;
        }         
        public String getString() {
            return null;
        }
    };  
    @Override
    public LazyNumber lazyEval(List<LazyNumber> lazyParams) {
        if (lazyParams.get(0).getString().equals(lazyParams.get(1).getString())) {
            return ZERO;
        }
        return ONE;
    }
});

e.eval(); // returns 1

How to contribute

How to make a clean pull request

  • Create a personal fork of EvalEx on GitHub.
  • Clone the fork on your local machine. Your remote repo on GitHub is called origin.
  • Add the original repository as a remote called upstream.
  • If you created your fork a while ago be sure to pull upstream changes into your local repository.
  • Create a new branch to work on. Branch from master.
  • Implement/fix your feature, comment your code.
  • Follow the code style of EvalEx (Google code style), including indentation.
  • If the project has tests run them!
  • Add unit tests that test your new code.
  • In general, avoid changing existing tests, as they also make sure the existing public API is unchanged.
  • Add or change the documentation as needed.
  • Squash your commits into a single commit with git's interactive rebase.
  • Push your branch to your fork on GitHub, the remote origin.
  • From your fork open a pull request in the correct branch. Target the EvalEx's master branch.
  • Once the pull request is approved and merged you can pull the changes from upstream to your local repo and delete your branch.
  • And last but not least: Always write your commit messages in the present tense. Your commit message should describe what the commit, when applied, does to the code – not what you did to the code.

Author and License

Copyright 2012 by Udo Klimaschewski

http://about.me/udo.klimaschewski

http://UdoJava.com

Thanks to all who contributed to this project: Contributors

The software is licensed under the MIT Open Source license ( see LICENSE file).

Similar Projects