Introduction

exp4j is capable of evaluating expressions and functions in the real domain. It's a small (40KB) library without any external dependencies, that implements Dijkstra's Shunting Yard Algorithm. exp4j comes with a standard set of built-in functions and operators. Additionally users are able to create custom operators and functions.

Version 0.4.0 API Changes

As of Version 0.4.0 exp4j has been completely rewritten and a lot of API changes have been introduced. Please don't hate me ;). The old version is still available. Im currently planning to supply a legacy-pack with wrappers for the old API, but I'm not sure that it is feasible. The rewrite was IMHO necessary to accommodate various features (e.g. implicit multiplication) and first of all: Improved performance.

Version 0.3.11

The former version of exp4j is available here

Examples

The examples are mostly taken from the test class ExpressionBuilderTest and use JUnit's assertXXX() methods to check for correct results.

Apache Maven

In order to include exp4j as a dependency using Apache Maven you can the following dependency declaration can be used

<dependency>
    <groupId>net.objecthunter</groupId>
    <artifactId>exp4j</artifactId>
    <version>0.4.8</version>
</dependency>

Usage

Evaluating an expression

In order to evaluate an expression. The ExpressionBuilder class can be used to create an Expression object which is capable of evaluation. Custom functions and custom operators can be set via calls to ExpressionBuilder.function() and ExpressionBuilder.operator(). Any given variables can be set on the Expression object returned by ExpressionBuilder.build() via calls to Expression.variable()

Example 1:

Evaluate a simple expression

Expression e = new ExpressionBuilder("3 * sin(y) - 2 / (x - 2)")
        .variables("x", "y")
        .build()
        .setVariable("x", 2.3)
        .setVariable("y", 3.14);
double result = e.evaluate();

Evaluating an expression asynchronously

For evaluating expressions asynchronously the user only has to supply a java.util.concurrent.ExecutorService.

Example 2:

Evaluate a simple expression asynchronously

ExecutorService exec = Executors.newFixedThreadPool(1);
Expression e = new ExpressionBuilder("3log(y)/(x+1)")
        .variables("x", "y")
        .build()
        .setVariable("x", 2.3)
        .setVariable("y", 3.14);
Future<Double> future = e.evaluateAsync(exec);
double result = future.get();

Variable declaration

Variable names must start with a letter or the underscore _ and can only include letters, digits or underscores.

the following are valid variable names:

  • varX
  • _x1
  • _var_X_1

    while 1_var_x is not as it does not start with a letter or a underscore.

Implicit multiplication

Since v0.4.0 exp4j does support implicit multiplication. Therefore an expression like 2cos(yx) will be interpreted as 2*cos(y*x)

Example 3

Using implicit muliplication

double result = new ExpressionBuilder("2cos(xy)")
        .variables("x","y")
        .build()
        .setVariable("x", 0.5d)
        .setVariable("y", 0.25d)
        .evaluate();
assertEquals(2d * Math.cos(0.5d * 0.25d), result, 0d);

Numerical constants

Since version 0.4.6 the following common constants have been added to exp4j and are bound automatically: pi, π the value of π as defined in Math.PI, e the value of Euler's number e, φ the value of the golden ratio (1.61803398874)

Example 4

Use constants in an expression

String expr = "pi+π+e+φ";
double expected = 2*Math.PI + Math.E + 1.61803398874d;
Expression e = new ExpressionBuilder(expr).build();
assertEquals(expected, e.evaluate(),0d);

Scientific notation

Since version 0.3.5 it is possible to use scientific notation for numbers see wikipedia. The number is split into a significand/mantissa y and exponent x of the form yEx which is evaluated as y * 10^x. Be aware that 'e/E' is no operator but part of a number and therefore an expression like 1.1e-(x*2) can not be evaluated. An example using the Fine-structure constant α=7.2973525698 * 10^−3:

Example 5

Use scientific notation in an expression

String expr = "7.2973525698e-3";
double expected = Double.parseDouble(expr);
Expression e = new ExpressionBuilder(expr).build();
assertEquals(expected, e.evaluate(),0d);

Custom functions

you can extend the abstract class Function in order to use custom functions in expressions. you only have to implement the apply(double... args) method. In the following example a function for the logarithm to the base 2 is created and used in a following expression.

Example 6

A custom function calculating the logarithm to an arbitrary base using the identity log(value, base) = ln(value)/ln(base)

Function logb = new Function("logb", 2) {
    @Override
    public double apply(double... args) {
        return Math.log(args[0]) / Math.log(args[1]);
    }
};
double result = new ExpressionBuilder("logb(8, 2)")
        .function(logb)
        .build()
        .evaluate();
double expected = 3;
assertEquals(expected, result, 0d);

you can also define multi argument functions like avg(a,b,c,d) via the constructor Function(String name, int argc), with argc being the argument count. The following example uses a custom function which returns the average of four values.

Example 7

A custom average function taking 4 arguments:

Function avg = new Function("avg", 4) {

    @Override
    public double apply(double... args) {
        double sum = 0;
        for (double arg : args) {
            sum += arg;
        }
        return sum / args.length;
    }
};
double result = new ExpressionBuilder("avg(1,2,3,4)")
        .function(avg)
        .build()
        .evaluate();

double expected = 2.5d;
assertEquals(expected, result, 0d);

Custom operators

you can extend the abstract class Operator in order to declare custom operators for use in expressions, with the symbol being a String consisting of !,#,§,$,&,;,:,~,<,>,|,=.. Be aware that adding a Operator with a used symbol overwrites any existing operators including the builtin ones. So it's possible to overwrite e.g. the + operator. The constructor of an Operator takes up to 4 arguments:

  • the symbol used for this operation (A String consisting of !,#,§,$,&,;,:,~,<,>,|,=)
  • if the operation is left associative
  • the precedence of the operation
  • the number of operands the operator has (1 or 2)

Example 8

Create a custom operator for calculating the factorial

Operator factorial = new Operator("!", 1, true, Operator.PRECEDENCE_POWER + 1) {

    @Override
    public double apply(double... args) {
        final int arg = (int) args[0];
        if ((double) arg != args[0]) {
            throw new IllegalArgumentException("Operand for factorial has to be an integer");
        }
        if (arg < 0) {
            throw new IllegalArgumentException("The operand of the factorial can not be less than zero");
        }
        double result = 1;
        for (int i = 1; i <= arg; i++) {
            result *= i;
        }
        return result;
    }
};

double result = new ExpressionBuilder("3!")
        .operator(factorial)
        .build()
        .evaluate();

double expected = 6d;
assertEquals(expected, result, 0d);

Example 9

Create a custom operator logical operator that returns 1 if the first argument is greater or equals than the second argument, and otherwise it returns 0.

@Test
public void testOperators3() throws Exception {
    Operator gteq = new Operator(">=", 2, true, Operator.PRECEDENCE_ADDITION - 1) {

        @Override
        public double apply(double[] values) {
            if (values[0] >= values[1]) {
                return 1d;
            } else {
                return 0d;
            }
        }
    };

    Expression e = new ExpressionBuilder("1>=2").operator(gteq)
            .build();
    assertTrue(0d == e.evaluate());
    e = new ExpressionBuilder("2>=1").operator(gteq)
            .build();
    assertTrue(1d == e.evaluate());

Built-in operators

  • Addition: 2 + 2
  • Subtraction: 2 - 2
  • Multiplication: 2 * 2
  • Division: 2 / 2
  • Exponentation: 2 ^ 2
  • Unary Minus,Plus (Sign Operators): +2 - (-2)
  • Modulo: 2 % 2

Precedence of unary minus and power operators

The precedence of the unary minus operator is lower than the precedence of the power operator. This means that an expression like -1^2 is evaluated as -(1^2) not as (-1)^2.

Division by zero in operations and functions

exp4j throws a ArithmeticException when a division by zero is attempted. When implementing Operator or Function involving divisions the implementor has to make sure that a corresponding RuntimeException is thrown.

Example 10

Throw an Arithmetic exception when a division by zero is attempted

Operator reciprocal = new Operator("$", 1, true, Operator.PRECEDENCE_DIVISION) {
    @Override
    public double apply(final double... args) {
        if (args[0] == 0d) {
            throw new ArithmeticException("Division by zero!");
        }
        return 1d / args[0];
    }
};
Expression e = new ExpressionBuilder("0$").operator(reciprocal).build();
e.evaluate(); // <- this call will throw an ArithmeticException

Built-in functions

  • abs: absolute value
  • acos: arc cosine
  • asin: arc sine
  • atan: arc tangent
  • cbrt: cubic root
  • ceil: nearest upper integer
  • cos: cosine
  • cosh: hyperbolic cosine
  • exp: euler's number raised to the power (e^x)
  • floor: nearest lower integer
  • log: logarithmus naturalis (base e)
  • log10: logarithm (base 10)
  • log2: logarithm (base 2)
  • sin: sine
  • sinh: hyperbolic sine
  • sqrt: square root
  • tan: tangent
  • tanh: hyperbolic tangent
  • signum: signum function

Validation of expression

Since version 0.4.0 exp4j has an added feature to validate expressions. Users can call Expression.validate() to perform a relatively fast validation. The validate method also accepts a boolean argument, indicating if a check for empty variables should be performed.

Example 11

Validate an expression

Expression e = new ExpressionBuilder("x")
        .variable("x")
        .build();

ValidationResult res = e.validate();
assertFalse(res.isValid());
assertEquals(1, res.getErrors().size());

e.setVariable("x",1d);
res = e.validate();
assertTrue(res.isValid());

Example 12

Validate an expression before variables have been set, i.e. skip checking if all variables have been set.

Expression e = new ExpressionBuilder("x")
        .variable("x")
        .build();

ValidationResult res = e.validate(false);
assertTrue(res.isValid());
assertNull(res.getErrors());

Error handling

The former versions of exp4j came with a set of Exceptions which had to be caught by the user, since this was often an unnecessary hassle, exp4j as of Version 0.4.0 only throws RuntimeExceptions and foremost IllegalArgumentExceptions from the standard Java API.

Tag Library

Thanks to Leo there is a Tag Library available at https://github.com/leogtzr/exp4j_tld. This enables users to use exp4j directly in JSP pages.

API changes

  • Version 0.4.3: Unicode names support added
  • Version 0.4.1: Future API introduced
  • Version 0.4.0: New Expression/ExpressionBuilder API
  • Version 0.3.2: choice for unary minus precedence via System property
  • Version 0.3.0: implicit variable declarations á la "f(x)=2x" have been removed
  • Version 0.3.0: PostfixExpression API has been removed
  • Version 0.3.0: The CustomOperator API has been added
  • Version 0.2.5: the argument type for CustomFunction changed from double to double[] with an array holding the arguments of the function call, in order to allow multi argument functions like max(x,y,z)
  • Version 0.2.2: the PostfixExpression API has been marked deprecated.
  • Version 0.2.1: the CustomFunction API has been added
  • Version 0.2.0: the ExpressionBuilder/Calculable API has been added