Frequently Asked Questions

Why is Jep giving inaccurate results?

You will notice that when you evaluate something as simple as "8 - 7.9" the result will be 0.09999999999999964 rather than 0.1. These inaccuracies are the result of floating point arithmetic. Internally, Jep uses the double type to represent numbers by default. Unfortunately, even for trivial calculations such as "8 - 7.9" the calculation can not be performed accurately.

You will notice the same if you run the following code.

double a=8, b=7.9;
System.out.println("a-b = " + (a-b));
//prints a-b = 0.09999999999999964

So this is not a Jep flaw, just a limitation of using floating point numbers. Although floating point numbers are accurate enough for many applications, these types of errors should not be ignored in applications where accuracy is critical.

By using System.out.printf(), results from Jep can be displayed to a given number of decimal places. For example

jep.parse("8-7.9");
double res = jep.evaluateD();
System.out.printf("%.3f",res);
This will print the result to three decimal places: 0.100. See java.util.Formatter for details on formatting output. The java.text.NumberFormat class can also be used to format results.

Jep has a round function, which can round arguments to a given number of decimal places. round(8-7.9,1) will round results to one decimal place.

To avoid this problem, you can also use the BigDecimal components as described in the BigDecimal section. They allow you to perform calculations with arbitrary accuracy unlike floating point arithmetic.

How should I handle NULL values?

If Jep is throwing an exception "EvaluationException: Could not evaluate XXX : no value set for the variable." during evaluation, then you are running into issues with null values.

It is important to note that by default variables whose values have not been set by either

jep.addVariable(name,value);

or by parsing an expression like

XXX = 123.456;

will remain initialized to null. So when you attempt to evaluate an expression with a null variable, the evaluator will by default throw an exception when the null value is encountered.

There are a several of ways around this:

  1. Depending on the evaluator you are using, you can turn off the check for null values with
    ((StandardEvaluator)jep.getEvaluator()).setTrapNullValues(false);
    or
    ((FastEvaluator)jep.getEvaluator()).setTrapNullValues(false);
    in which case null values will be passed to the individual functions.
  2. You can change the default value used for unassigned variables with
    jep.getVariableFactory().setDefaultValue(Double.valueOf(0.0));
  3. A more advanced treatment of null values would be to create a type to represent genuine null values, create a constant NULL to recognise NULL in the parser and explicitly set NULL variables.
      Object MyNULL = new Object(); // create a null value
      jep.addConstant("NULL",MyNULL); // define a constant in the parser
      jep.addVariable("x",MyNULL); // add a variable with the NULL value
      jep.parse("y=NULL"); // expression assigning a variable to NULL
      jep.parse("x==NULL"); // Compare a variable to the NULL value
      
  4. Since version 3.4, the src.com.singularsys.jep.misc.nullwrapper package allows for null values to be treated like NULL in an SQL database. Any function or operator with null as an argument will return null. For instance null+5 will be null. The package takes existing operators and functions and adds a wrapper class which will test if a null value is input; if so, null is returned, otherwise the original function is used. The NullWrappedOperatorTable and NullWrappedFunctionTable wrap all the existing operators and functions. To set this up use
    jep.setComponent(new NullWrappedOperatorTable(
                      (OperatorTable2) jep.getOperatorTable(),true));
    jep.setComponent(new NullWrappedFunctionTable(jep.getFunctionTable()));
    jep.setComponent(new StandardConfigurableParser());
    ((FastEvaluator) jep.getEvaluator()).setTrapNullValues(false);
      

    The operator table also adds an additional operator, the null safe equals <=> which works like equals, but if both arguments are null will return true. This needs the StandardConfigurableParser for the operator to be recognized when parsing.

    The logical operators work like those in SQL, so null || true is true, null || false is null, null && true is null and null && false is false. See NullWrappedLazyLogical for the complete truth tables.

    The If function just tests the first argument for null, returning null if it is. If the first argument is true or false it will return the second or third argument respectively, these arguments can be null.

    The names of classes have been changed in version 3.5 and they generally begin NullWrapped....

Why am I getting an EvaluationException?

There can be several reasons for EvaluationExceptions to be thrown. Of course it is best to look at the message in the exception for more details.

If you are seeing

com.singularsys.jep.EvaluationException: Could not evaluate XXX : no
    value set for the variable.

then refer to "How should I handle NULL values?" in this FAQ.

Other evaluation exceptions are typically due functions or operators not being compatible with the data types passed in. Since Jep only checks data types during evaluation, you will not see these issues while parsing an expression.

How can I allow variable names with a dot in them?

To do this, you need to use the Configurable Parser which allows great control over the input syntax. The following code sets up the parser with the standard options but using identifiers with a dot in them as well by using the factory method com.singularsys.jep.configurableparser.matchers.IdentifierTokenMatcher.dottedIndetifierMatcher(). The final step in setting up is to remove the dot-product "." operator to prevent any ambiguity.

ConfigurableParser cp = new ConfigurableParser();
cp.addHashComments();
cp.addSlashComments();
cp.addDoubleQuoteStrings();
cp.addWhiteSpace();
cp.addExponentNumbers();
cp.addOperatorTokenMatcher();
cp.addSymbols("(",")","[","]",",");
cp.setImplicitMultiplicationSymbols("(","[");

// Sets it up for identifiers with dots in them.
cp.addTokenMatcher(IdentifierTokenMatcher.dottedIndetifierMatcher());

cp.addSemiColonTerminator();
cp.addWhiteSpaceCommentFilter();
cp.addBracketMatcher("(",")");
cp.addFunctionMatcher("(",")",",");
cp.addListMatcher("[","]",",");
cp.addArrayAccessMatcher("[","]");

// Construct the Jep instance and set the parser
jep = new Jep(cp);

// Remove the dot operator
OperatorTable2 ot = (OperatorTable2)jep.getOperatorTable();
ot.removeOperator(ot.getDot());
//notify other components of change in operator table
jep.reinitializeComponents();

How can I reduce the size of Jep?

The memory footprint of Jep can be reduced in a number of ways.

For example you could create a Jep instance with no loaded functions with:

Jep jep1 = new Jep(new FunctionTable(), new StandardConfigurableParser()); 
And you can create a Jep instance without parser, without functions and without the ability to print with:
Jep jep2 = new Jep(new FunctionTable(), NullParser.NULL_PARSER, PrintVisitor.NULL_PRINT_VISITOR);

The com.singularsys.jepexamples.diagnostics.ExamineSizes program gives give a detailed breakdown of memory usage.

To reduce the disk space the com/singularsys/jep/misc, com/singularsys/jep/walker, com/singularsys/jep/bigdecimal packages are all optional. None of the classes in the com/singularsys/jepexamples or com/singularsys/jeptest directories are needed. If the configurable parser is used then in the com/singularsys/jep/parser directory the JccParser* classes can all be skipped. Alternatively if the standard parser is used then the com/singularsys/jep/configurableparser package can be skipped completely.

The jep-java-3.5-min.jar jar file is a fairly small example with a full set of functions and a working console application. which is less that 200K in size.

How to work with very large expressions

The size of expressions which can be worked with is mainly limited by the stack size allocated to java. This can be increased by using the -Xss512k argument to the Java VM. The ConfigurableParser can generally parse larger expressions than the standard parser. The PostfixEvaluator has been written to not make heavy use of the stack and can evaluate expressions with over a million nodes. The DestructiveTests application tests the size of expressions which can be parsed and evaluated.