MathOption
When performing mathematical operations on MJ variables, the applicable options from the enumeration MJVariable.MathOption affect some important aspects of those operations. The options are listed and explained below.
EVALUATE_JAVA_TYPE |
Use Java value (e.g., intValue() , decimalValue() ) when
evaluating operands of mathematical operations. |
EVALUATE_MAPPER_TYPE |
Use Mapper value (i.e., toMapperNumber() ) when
evaluating operands of mathematical operations. |
USE_ZERO_FOR_NULL |
If operand of mathematical operation has NULL value, treat as zero during evaluation. |
RETURN_EXACT |
Return result as exact, whole number only, report error if truncation of fractional digits would occur. |
PREFER_DECIMAL_OVER_FLOAT |
Convert string literals containing real numbers (e.g., '3.14') to BigDecimal ,
compute fractional numbers as BigDecimal during operation and return
fractional result as BigDecimal . |
PREFER_FLOAT_OVER_DECIMAL |
Convert string literals containing real numbers (e.g., '3.14') to Double ,
compute fractional numbers as Double during operation and return
fractional result as Double . |
MJ variable methods that implement mathematical operations and
math formulas both
support MathOption
, and
each method (or formula) has a default set of MathOption
values;
those default options are specified in the Javadoc.
EVALUATE_JAVA_TYPE
vs. EVALUATE_MAPPER_TYPE
Options EVALUATE_JAVA_TYPE
and EVALUATE_MAPPER_TYPE
call
out a distinction in mathematical calculations
carried out by @ART
and @CHG
. The difference is
often a subtle one, but is illustrated by this example:
// @LDV <target>f3.2=1.71 . Stored as 1.71, displayed as '1.7' [rounded-down]
MJDecimal target = new MJDecimal(VariableScope.LOCAL, 3, 2, new BigDecimal("1.71"));
// @LDV <incr>f3.2=8.76 . Stored as 8.76, displayed as '8.8' [rounded-up]
MJDecimal incr = new MJDecimal(VariableScope.LOCAL, 3, 2, new BigDecimal("8.76"));
// @ART <target>+<incr> <rsltArt>f7.3 . Uses displayed values: 1.7 + 8.8
Number sumArt = target.add(incr, EnumSet.of(MathOption.EVALUATE_MAPPER_TYPE));
assert sumArt instanceof BigDecimal;
MJDecimal rsltArt = new MJDecimal(VariableScope.LOCAL, 7, 3, (BigDecimal) sumArt);
assert "10.500".equals(rsltArt.toMapperNumericString());
// @CHG <rsltChg>f7.3 <target> + <incr> . Uses stored values: 1.71 + 8.76
Number sumChg = target.add(incr, EnumSet.of(MathOption.EVALUATE_JAVA_TYPE));
assert sumChg instanceof BigDecimal;
MJDecimal rsltChg = new MJDecimal(VariableScope.LOCAL, 7, 3, (BigDecimal) sumChg);
assert "10.470".equals(rsltChg.toMapperNumericString());
Different results are obtained although the same variables are summed because
@ART
adds the "display value"
of the variables while @CHG
adds the primitive, raw values of the variables. The enumeration
name EVALUATE_MAPPER_TYPE
denotes use of the Mapper
value (toMapperNumber()
) of a variable when evaluating operands of mathematical operations, and should
always be used for @ART
calculations. Similarly,
EVALUATE_JAVA_TYPE
denotes use of the raw, Java primitive value of
a variable, and should always be used for @CHG
calculations.
USE_ZERO_FOR_NULL
This option allow zero to "stand in" for variables whose value is NULL during a mathematical operation:
// @LDV <target>f3.2=1.71 .
MJDecimal target = new MJDecimal(VariableScope.LOCAL, 3, 2, new BigDecimal("1.71"));
// @INC,999.5 <target> .
target.increment(new BigDecimal("999.5"));
// <target> displayed by MAPPER as '***' since 1001 does not fit in F3.2
assert 100100 == target.decimalValue().unscaledValue().longValue();
assert null == target.toMapperNumber();
// @ART <target>+1 <rslt>f9.3
// @. MAPPER fails on above with "There is an invalid character in this expression",
// @. since @ART uses display value of <target> in calculation, which is '***'
try {
Number sum = target.add(1L, EnumSet.of(MathOption.EVALUATE_MAPPER_TYPE));
}
catch (MJExecuteException ex) {
// "cannot add() to variable 1001.00: variable is NULL and USE_ZERO_FOR_NULL not specified"
ex.printStackTrace();
}
// Try again where NULL returned from target.toMapperNumber() is treated as zero,
// essentially becoming @ART 0+1.
Number sum = target.add(1L, EnumSet.of(MathOption.EVALUATE_MAPPER_TYPE,
MathOption.USE_ZERO_FOR_NULL));
assert sum instanceof BigDecimal;
MJDecimal rslt = new MJDecimal(VariableScope.LOCAL, 9, 3, (BigDecimal) sum);
assert "1.000".equals(rslt.toMapperNumericString());
RETURN_EXACT
Similarly to java.math.BigDecimal.longValueExact
, this option throws an exception if
the result of mathematical operation contains a fractional part (therefore, use RETURN_EXACT
only when the result is expected to be a whole number):
// @LDV <dvr>f1=3 .
MJDecimal dvr = new MJDecimal(VariableScope.LOCAL, 1, 0, new BigDecimal("3"));
// @CHG <quot>f9.3 10 / <dvr> .
try {
Number quot = MJMath.divide(new Integer(10), dvr,
EnumSet.of(MathOption.EVALUATE_JAVA_TYPE, MathOption.RETURN_EXACT));
}
catch (MJExecuteException ex) {
// "MathOption.RETURN_EXACT specified but 10 divided by 3 has fractional part"
ex.printStackTrace();
}
PREFER_DECIMAL_OVER_FLOAT
vs. PREFER_FLOAT_OVER_DECIMAL
When mathematical operations on whole integers (division, for example) result in a fractional number,
these options specify which Number
-derived class stores the result,
java.math.BigDecimal
(PREFER_DECIMAL_OVER_FLOAT
) or
java.math.Double
(PREFER_FLOAT_OVER_DECIMAL
). Applicable mostly
to MJInteger
and MJMath
methods that carry out division, but
consult the Javadoc to determine exactly which methods support these options:
// @LDV <dvd>i4=22, <dvi>i4=7 . Whole integers
MJInteger dvd = new MJInteger(VariableScope.LOCAL, 4, EnumSet.noneOf(LoadOption.class), "22");
MJInteger dvi = new MJInteger(VariableScope.LOCAL, 4, EnumSet.noneOf(LoadOption.class), "7");
// @ART <dvd>/<dvi> <quot>f18.17 .
Number decQuot = dvd.divide(dvi, EnumSet.of(MathOption.EVALUATE_MAPPER_TYPE,
MathOption.PREFER_DECIMAL_OVER_FLOAT));
assert decQuot instanceof BigDecimal;
// @ART <dvd>/<dvi> <quot>f18.17 . Same as above, except result is java.lang.Double
Number dblQuot = dvd.divide(dvi, EnumSet.of(MathOption.EVALUATE_MAPPER_TYPE,
MathOption.PREFER_FLOAT_OVER_DECIMAL));
assert dblQuot instanceof Double;