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);
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, use the BigDecimal components as described in the BigDecimal section. They allow you to perform calculations with arbitrary accuracy unlike floating point arithmetic.
If Jep is throwing an exception
"EvaluationException: Could not evaluate XXX : no
value set for the variable." during evaluation, then you are running in to issues with null values.
It is important to note that by default variables whose values have not been set by either
or by parsing an expression like
XXX = 123.456;
will remain initialized to null. So when you attempt to evaluate and 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:
((FastEvaluator)jep.getEvaluator()).setTrapNullValues(false);in which case null values will be passed to the individual functions.
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
nullas an argument will return
null. For instance
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
NullWrappedFunctionTablewrap 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 to 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
for the complete truth tables.
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
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.
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();
The memory footprint of Jep can be reduced in a number of ways.
StandardConfigurableParserrather than the default parser. This takes considerably less runtime space (4K) compared to 51K.
NullParserwhich take virtually no space. This can be used with SerializableExpression where a compact representation of an expression can stored on disk.
FunctionTablecomponent rather than the
StandardFunctionTable. Any functions needed can then be individually loaded using
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/bigdecimal packages are all optional.
None of the classes in the
com/singularsys/jeptest directories are needed.
If the configurable parser is used then in the
com/singularsys/jep/parser directory the
classes can all be skipped.
Alternatively if the standard parser is used then the
com/singularsys/jep/configurableparser package can be skipped completely.
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.
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.
ConfigurableParser can generally parse larger expressions than the standard parser.
been written to not make heavy use of the stack and can evaluate expressions with over a million nodes.
application tests the size of expressions which can be parsed and evaluated.