This is an automated email from the ASF dual-hosted git repository.
slawrence pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil.git
The following commit(s) were added to refs/heads/main by this push:
new 6650eb918 Correct numeric operations
6650eb918 is described below
commit 6650eb918e370cfb4eba1809939aca91684572cc
Author: Steve Lawrence <[email protected]>
AuthorDate: Tue Oct 7 11:51:22 2025 -0400
Correct numeric operations
The current logic of numeric operations using in DFDL expressions is not
correct in a couple of ways
First, numeric operations using shorts and bytes are just broken and can
least to ClassCastExceptions. The main reason is that Java promotes
short and byte operations to ints but our logic did not account for
that, so data values had ints when we expected short/byte.
Second, the currently logic does not correctly implement the XPath 2.0
specification for numeric operations. For example, if two arguments have
a least upper bound of SignedNumeric we cast them to Double. An example
where this breaks if xs:double and xs:decimal, which has a least upper
bound of SignedNumeric. These should should be promoted to Decimal
instead of Double. We also did not handle underflow/overflow correctly.
For example, xs:long(xs:byte(255) + xs:byte(1)) results in an out of
range error rather than correctly returning xs:long(256).
This changes our numeric operations fix these problems and match the
XPath 2.0 spec.
To achieve this we first promote numeric operands to one of decimal,
integer, double, float, or long, based on the least upper bound of the
operand types. Java does not perform any type promotion with these, so
it avoids accidental changes of expected types. Note that we long where
possible to maintain performant operations, rather than simply promoting
everything to xs:integer as implied by the XPath 2.0 specification. This
does mean underflow/overflow is possible, but this is much less likely.
Note that such overflow will not be detected in intermediate operations,
but will be detect if the result does not fit withing the range of the
target type.
This also changes the results of div and idv operations to return
Decimal/Float/Double or Integer/Long for similar reasons.
This means many of the NumericOps can not be removed (e.g. PlusShort)
since the set of types we perform operations on is much smaller. We also
update the NumericOps implementations to use DataValue's, since we know
exactly what the types should be, rather than relying on asFoo to
convert the operand to the expected type.
This also changes the resulting type of numeric if-branches. This are
only promoted to the least upper bound rather than also doing the
numeric operation conversions. The numeric results can will be promoted
if then used in a numeric operation.
DAFFODIL-2574
---
.../apache/daffodil/core/dpath/Expression.scala | 75 +----
.../apache/daffodil/core/dpath/NodeInfoUtils.scala | 93 +++---
.../daffodil/runtime1/dpath/DPathRuntime.scala | 12 +-
.../daffodil/runtime1/dpath/NumericOps.scala | 316 +++++++--------------
.../section23/dfdl_expressions/expressions.tdml | 72 ++++-
.../section23/dfdl_expressions/expressions2.tdml | 2 +-
.../section23/dfdl_expressions/functions.tdml | 10 +-
.../section23/dfdl_functions/Functions.tdml | 4 +-
.../dfdl_expressions/TestDFDLExpressions.scala | 12 +
9 files changed, 269 insertions(+), 327 deletions(-)
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala
index e749ad437..94d6e7cef 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala
@@ -331,20 +331,6 @@ trait NumericExpression extends BinaryExpMixin {
case ("idiv", Integer) => IDivInteger
case ("mod", Integer) => ModInteger
- case ("+", NonNegativeInteger) => PlusNonNegativeInteger
- case ("-", NonNegativeInteger) => MinusNonNegativeInteger
- case ("*", NonNegativeInteger) => TimesNonNegativeInteger
- case ("div", NonNegativeInteger) => DivNonNegativeInteger
- case ("idiv", NonNegativeInteger) => IDivNonNegativeInteger
- case ("mod", NonNegativeInteger) => ModNonNegativeInteger
-
- case ("+", UnsignedLong) => PlusUnsignedLong
- case ("-", UnsignedLong) => MinusUnsignedLong
- case ("*", UnsignedLong) => TimesUnsignedLong
- case ("div", UnsignedLong) => DivUnsignedLong
- case ("idiv", UnsignedLong) => IDivUnsignedLong
- case ("mod", UnsignedLong) => ModUnsignedLong
-
case ("+", Long) => PlusLong
case ("-", Long) => MinusLong
case ("*", Long) => TimesLong
@@ -352,55 +338,6 @@ trait NumericExpression extends BinaryExpMixin {
case ("idiv", Long) => IDivLong
case ("mod", Long) => ModLong
- case ("+", UnsignedInt) => PlusUnsignedInt
- case ("-", UnsignedInt) => MinusUnsignedInt
- case ("*", UnsignedInt) => TimesUnsignedInt
- case ("div", UnsignedInt) => DivUnsignedInt
- case ("idiv", UnsignedInt) => IDivUnsignedInt
- case ("mod", UnsignedInt) => ModUnsignedInt
-
- case ("+", ArrayIndex) => PlusUnsignedInt
- case ("-", ArrayIndex) => MinusUnsignedInt
- case ("*", ArrayIndex) => TimesUnsignedInt
- case ("div", ArrayIndex) => DivUnsignedInt
- case ("idiv", ArrayIndex) => IDivUnsignedInt
- case ("mod", ArrayIndex) => ModUnsignedInt
-
- case ("+", Int) => PlusInt
- case ("-", Int) => MinusInt
- case ("*", Int) => TimesInt
- case ("div", Int) => DivInt
- case ("idiv", Int) => IDivInt
- case ("mod", Int) => ModInt
-
- case ("+", UnsignedShort) => PlusUnsignedShort
- case ("-", UnsignedShort) => MinusUnsignedShort
- case ("*", UnsignedShort) => TimesUnsignedShort
- case ("div", UnsignedShort) => DivUnsignedShort
- case ("idiv", UnsignedShort) => IDivUnsignedShort
- case ("mod", UnsignedShort) => ModUnsignedShort
-
- case ("+", Short) => PlusShort
- case ("-", Short) => MinusShort
- case ("*", Short) => TimesShort
- case ("div", Short) => DivShort
- case ("idiv", Short) => IDivShort
- case ("mod", Short) => ModShort
-
- case ("+", UnsignedByte) => PlusUnsignedByte
- case ("-", UnsignedByte) => MinusUnsignedByte
- case ("*", UnsignedByte) => TimesUnsignedByte
- case ("div", UnsignedByte) => DivUnsignedByte
- case ("idiv", UnsignedByte) => IDivUnsignedByte
- case ("mod", UnsignedByte) => ModUnsignedByte
-
- case ("+", Byte) => PlusByte
- case ("-", Byte) => MinusByte
- case ("*", Byte) => TimesByte
- case ("div", Byte) => DivByte
- case ("idiv", Byte) => IDivByte
- case ("mod", Byte) => ModByte
-
case ("+", Float) => PlusFloat
case ("-", Float) => MinusFloat
case ("*", Float) => TimesFloat
@@ -414,7 +351,15 @@ trait NumericExpression extends BinaryExpMixin {
case ("div", Double) => DivDouble
case ("idiv", Double) => IDivDouble
case ("mod", Double) => ModDouble
- case _ => subsetError("Unsupported operation '%s' on type %s.", op,
convergedArgType)
+
+ // Note that we intentionally do not match on all possible types here.
Numeric operations
+ // should always converge/are promoted to one of the above types
+ // $COVERAGE-OFF$
+ case _ =>
+ Assert.invariantFailed(
+ "Numeric operands did not converge to a supported type: " +
convergedArgType
+ )
+ // $COVERAGE-ON$
}
}
@@ -572,7 +517,7 @@ case class IfExpression(ifthenelse: List[Expression])
extends ExpressionLists(if
override lazy val inherentType = {
(thenPart.inherentType, elsePart.inherentType) match {
case (left: NodeInfo.Numeric.Kind, right: NodeInfo.Numeric.Kind) =>
- NodeInfoUtils.generalizeArgAndResultTypesForNumericOp(op, left,
right)._2
+ NodeInfoUtils.typeLeastUpperBound(left, right)
case (left, right) if left == right => left
case (left, right) if right == NodeInfo.Nothing => left
case (left, right) if left == NodeInfo.Nothing => right
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/NodeInfoUtils.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/NodeInfoUtils.scala
index 450f75d03..931332835 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/NodeInfoUtils.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/NodeInfoUtils.scala
@@ -90,49 +90,68 @@ object NodeInfoUtils {
Numeric.Kind // second result is generalized result type
) = {
- /*
- * Adjust for the Decimal result type when div/idiv is used
- */
- def divResult(resultType: NodeInfo.Numeric.Kind) = resultType match {
- case ArrayIndex => ArrayIndex
- case _: Decimal.Kind => Decimal
+ // When doing arithmetic, we first find the least upper bound of the left
and right arg
+ // types. If the least upper bound can fit into a long, we promote both
args to a long,
+ // which reduces the likelihood of accidental overflow/underflow during
intermediate
+ // arithmetic operations, especially with smaller types like xs:byte. The
long result will
+ // likely need to be downcast to the final result type at the end of the
expression, at
+ // which point we check for overflow/underflow errors.
+ val lub = NodeInfoUtils.typeLeastUpperBound(leftArgType, rightArgType)
+ val argType = lub match {
+ case ArrayIndex => Long
+ case SignedNumeric => Decimal // lub of float/double and other types
+ case Decimal => Decimal
case Double => Double
case Float => Float
- case _ => Assert.usageError("Unsupported return type:
%s".format(resultType))
+ case _: Long.Kind => Long
+ case _: UnsignedInt.Kind => Long
+ case _: Numeric.Kind => Integer
+ // $COVERAGE-OFF$
+ case _ =>
+ Assert.invariantFailed(
+ s"least upper bound of $leftArgType and $rightArgType was not a
numeric: $lub"
+ )
+ // $COVERAGE-ON$
}
- def idivResult(resultType: NodeInfo.Numeric.Kind) = resultType match {
- case Decimal => Integer
- case Integer => Integer
- case Double => Long
- case Long => Long
- case Float => Int
- case Int => Int
- case Short => Short
- case ArrayIndex => ArrayIndex
- case _ => Assert.usageError("Unsupported return type:
%s".format(resultType))
+ // Determine the return type of the numeric operation.
+ //
+ // Division returns either Decimal, Double, or Float. Note that we return
Double when the
+ // converged arg type is Long to ensure reasonable precision and
performance. We avoid using
+ // Decimal since that has additional overhead that we try to avoid unless
a schema
+ // explicitly uses larger Decimal/Integer/etc types.
+ //
+ // Integer division returns Integer or Long, again preferring Long for
performance reasons
+ // unless a schema explicitly uses larger types.
+ //
+ // All other numeric operations return the same type as the converged args
+ val resultType = op match {
+ case "div" =>
+ argType match {
+ case Decimal => Decimal
+ case Integer => Decimal
+ case Double => Double
+ case Float => Float
+ case Long => Double
+ // $COVERAGE-OFF$
+ case _ => Assert.invariantFailed(s"unexpected arg type: $argType")
+ // $COVERAGE-ON$
+ }
+ case "idiv" =>
+ argType match {
+ case Decimal => Integer
+ case Integer => Integer
+ case Double => Long
+ case Float => Long
+ case Long => Long
+ // $COVERAGE-OFF$
+ case _ => Assert.invariantFailed(s"unexpected arg type: $argType")
+ // $COVERAGE-ON$
+ }
+ case _ => argType
}
- val (argType, resultType) = {
- val lub = NodeInfoUtils.typeLeastUpperBound(leftArgType, rightArgType)
- //
- // For each abstract type that could be the least upper bound of the two
- // arg types, we must pick a concrete type to convert everything into.
- val lubImplementationType = lub match {
- case SignedNumeric => NodeInfo.Double
- case _ => lub
- }
- (lubImplementationType, lubImplementationType)
- } match {
- case (argType: Numeric.Kind, resultType: Numeric.Kind) => (argType,
resultType)
- case x => Assert.invariantFailed(s"Expected Numeric.Kind. Found
${x.getClass}")
- }
- val res = op match {
- case "div" => (argType, divResult(resultType))
- case "idiv" => (argType, idivResult(resultType))
- case _ => (argType, resultType)
- }
- res
+ (argType, resultType)
}
def typeLeastUpperBound(left: NodeInfo.Kind, right: NodeInfo.Kind):
NodeInfo.Kind = {
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/dpath/DPathRuntime.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/dpath/DPathRuntime.scala
index e0f303938..19a2a79cd 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/dpath/DPathRuntime.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/dpath/DPathRuntime.scala
@@ -17,7 +17,6 @@
package org.apache.daffodil.runtime1.dpath
-import java.lang.Number as JNumber
import scala.collection.immutable.ArraySeq
import scala.xml.NodeSeq.seqToNodeSeq
@@ -31,6 +30,7 @@ import
org.apache.daffodil.runtime1.dsom.SchemaDefinitionDiagnosticBase
import org.apache.daffodil.runtime1.dsom.SchemaDefinitionError
import org.apache.daffodil.runtime1.infoset.DINode
import org.apache.daffodil.runtime1.infoset.DataValue
+import org.apache.daffodil.runtime1.infoset.DataValue.DataValueNumber
import org.apache.daffodil.runtime1.infoset.DataValue.DataValuePrimitive
import
org.apache.daffodil.runtime1.infoset.DataValue.DataValuePrimitiveNullable
import org.apache.daffodil.runtime1.infoset.DataValue.DataValueString
@@ -288,10 +288,10 @@ case class NumericOperator(nop: NumericOp, left:
CompiledDPath, right: CompiledD
override def run(dstate: DState): Unit = {
val savedNode = dstate.currentNode
left.run(dstate)
- val leftValue = dstate.currentValue.getNumber
+ val leftValue = dstate.currentValue.getNonNullable
dstate.setCurrentNode(savedNode)
right.run(dstate)
- val rightValue = dstate.currentValue.getNumber
+ val rightValue = dstate.currentValue.getNonNullable
val result = nop.operate(leftValue, rightValue)
dstate.setCurrentValue(result)
}
@@ -299,11 +299,7 @@ case class NumericOperator(nop: NumericOp, left:
CompiledDPath, right: CompiledD
trait NumericOp {
- /**
- * It is such a pain that there is no scala.math.Number base class above
- * all the numeric types.
- */
- def operate(v1: JNumber, v2: JNumber): JNumber
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueNumber
}
abstract class Converter extends RecipeOp {
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/dpath/NumericOps.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/dpath/NumericOps.scala
index e05ffa5d7..34f75ef3f 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/dpath/NumericOps.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/dpath/NumericOps.scala
@@ -17,24 +17,29 @@
package org.apache.daffodil.runtime1.dpath
-import java.lang.Number as JNumber
-import java.math.{ BigDecimal as JBigDecimal, BigInteger as JBigInt }
+import java.math.BigDecimal as JBigDecimal
+import java.math.MathContext
-import org.apache.daffodil.lib.util.Numbers.*
+import org.apache.daffodil.runtime1.infoset.DataValue.DataValueBigDecimal
+import org.apache.daffodil.runtime1.infoset.DataValue.DataValueBigInt
+import org.apache.daffodil.runtime1.infoset.DataValue.DataValueDouble
+import org.apache.daffodil.runtime1.infoset.DataValue.DataValueFloat
+import org.apache.daffodil.runtime1.infoset.DataValue.DataValueLong
+import org.apache.daffodil.runtime1.infoset.DataValue.DataValuePrimitive
case object PlusDecimal extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigDecimal = {
- asBigDecimal(v1).add(asBigDecimal(v2))
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive):
DataValueBigDecimal = {
+ v1.getBigDecimal.add(v2.getBigDecimal)
}
}
case object MinusDecimal extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigDecimal = {
- asBigDecimal(v1).subtract(asBigDecimal(v2))
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive):
DataValueBigDecimal = {
+ v1.getBigDecimal.subtract(v2.getBigDecimal)
}
}
case object TimesDecimal extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigDecimal = {
- asBigDecimal(v1).multiply(asBigDecimal(v2))
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive):
DataValueBigDecimal = {
+ v1.getBigDecimal.multiply(v2.getBigDecimal)
}
}
@@ -52,15 +57,15 @@ case object TimesDecimal extends NumericOp {
* What's questionable here, is to what number of fraction digits it will
round.
* That can be specified also as an argument to divide(), but we have no
* information here (or anywhere really) about what precision is desired.
- * So we're omitting that and just saying "round it".
*
- * Really, there's no rounding scale/precision until we're ready to represent
- * the number in a string. In this case we're not. We're in the middle of an
- * expression, just happen to have two BigDecimal operands, and we're dividing
- * them, which should produce a BigDecimal result.
+ * Due to type promotion, it's actually fairly easy to write a simple
expression
+ * that leads to this error. For example, the expression "xs:int(1) div
xs:double(.75)"
+ * promotes both args to xs:decimal, and that division leads to non-terminating
+ * decimal expansions. It is likely too difficult to require users to round
these
+ * basic expressions, so our implementation must handle this.
*
* DFDL expressions are supposed to be consistent with XPath, so we look there
- * for suggestions. The XPath spec
+ * for what our implementation should do. The XPath spec
* [[https://www.w3.org/TR/xpath-functions-3/#op.numeric]] says it is
implementation
* defined.
*
@@ -76,276 +81,173 @@ case object TimesDecimal extends NumericOp {
* implementation retains for that operation, the result is truncated
* or rounded in an ·implementation-defined· manner
*
- * In our case, the implementation does what the JVM and java libraries do
when
- * divide() is called with two arguments the second of which specifies
- * to round half-up.
+ * So as long as we support 18 digits, our implementation should be in
+ * conformance with the spec. To allow for high precision, our implementation
+ * chooses to use MathContext.DECIMAL128, which provides 34 digits of precision
+ * and a rounding mode of half-even.
*/
case object DivDecimal extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigDecimal = {
- val v2bd = asBigDecimal(v2)
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive):
DataValueBigDecimal = {
+ val v2bd = v2.getBigDecimal
if (v2bd.compareTo(JBigDecimal.ZERO) == 0) {
throw new ArithmeticException("/ by zero")
}
- asBigDecimal(v1).divide(v2bd)
+ v1.getBigDecimal.divide(v2bd, MathContext.DECIMAL128)
}
}
case object IDivDecimal extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(DivDecimal.operate(v1, v2)) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueBigInt
= {
+ DivDecimal.operate(v1, v2).getBigDecimal.toBigInteger
+ }
}
case object ModDecimal extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigDecimal = {
- asBigDecimal(v1).remainder(asBigDecimal(v2))
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive):
DataValueBigDecimal = {
+ v1.getBigDecimal.remainder(v2.getBigDecimal)
}
}
case object PlusInteger extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(v1).add(asBigInt(v2)) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueBigInt
= {
+ v1.getBigInt.add(v2.getBigInt)
+ }
}
case object MinusInteger extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(v1).subtract(asBigInt(v2)) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueBigInt
= {
+ v1.getBigInt.subtract(v2.getBigInt)
+ }
}
case object TimesInteger extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(v1).multiply(asBigInt(v2)) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueBigInt
= {
+ v1.getBigInt.multiply(v2.getBigInt)
+ }
}
case object DivInteger extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigDecimal = {
DivDecimal.operate(v1, v2) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive):
DataValueBigDecimal = {
+ DivDecimal.operate(new JBigDecimal(v1.getBigInt), new
JBigDecimal(v2.getBigInt))
+ }
}
case object IDivInteger extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(DivInteger.operate(v1, v2)) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueBigInt
= {
+ v1.getBigInt.divide(v2.getBigInt)
+ }
}
case object ModInteger extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(v1).mod(asBigInt(v2)) }
-}
-
-case object PlusNonNegativeInteger extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(v1).add(asBigInt(v2)) }
-}
-case object MinusNonNegativeInteger extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(v1).subtract(asBigInt(v2)) }
-}
-case object TimesNonNegativeInteger extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(v1).multiply(asBigInt(v2)) }
-}
-case object DivNonNegativeInteger extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigDecimal = {
DivDecimal.operate(v1, v2) }
-}
-case object IDivNonNegativeInteger extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
- asBigInt(DivNonNegativeInteger.operate(v1, v2))
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueBigInt
= {
+ v1.getBigInt.mod(v2.getBigInt)
}
}
-case object ModNonNegativeInteger extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(v1).mod(asBigInt(v2)) }
-}
-
-case object PlusUnsignedLong extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(v1).add(asBigInt(v2)) }
-}
-case object MinusUnsignedLong extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(v1).subtract(asBigInt(v2)) }
-}
-case object TimesUnsignedLong extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(v1).multiply(asBigInt(v2)) }
-}
-case object DivUnsignedLong extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigDecimal = {
DivDecimal.operate(v1, v2) }
-}
-case object IDivUnsignedLong extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(DivUnsignedLong.operate(v1, v2)) }
-}
-case object ModUnsignedLong extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigInt = {
asBigInt(v1).mod(asBigInt(v2)) }
-}
case object PlusLong extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asLong(v1) + asLong(v2) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueLong =
{
+ v1.getLong + v2.getLong
+ }
}
case object MinusLong extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asLong(v1) - asLong(v2) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueLong =
{
+ v1.getLong - v2.getLong
+ }
}
case object TimesLong extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asLong(v1) * asLong(v2) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueLong =
{
+ v1.getLong * v2.getLong
+ }
}
case object DivLong extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JBigDecimal = {
DivDecimal.operate(v1, v2) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueDouble
= {
+ val v2l = v2.getLong
+ if (v2l == 0) {
+ throw new ArithmeticException("/ by zero")
+ }
+ v1.getLong.toDouble / v2l
+ }
}
case object IDivLong extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = {
asLong(DivLong.operate(v1, v2)) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueLong =
{
+ v1.getLong / v2.getLong
+ }
}
case object ModLong extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asLong(v1) % asLong(v2) }
-}
-
-case object PlusUnsignedInt extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asLong(v1) + asLong(v2) }
-}
-case object MinusUnsignedInt extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asLong(v1) - asLong(v2) }
-}
-case object TimesUnsignedInt extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asLong(v1) * asLong(v2) }
-}
-case object DivUnsignedInt extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { DivDecimal.operate(v1,
v2) }
-}
-case object IDivUnsignedInt extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = {
asLong(DivUnsignedInt.operate(v1, v2)) }
-}
-case object ModUnsignedInt extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asLong(v1) % asLong(v2) }
-}
-
-case object PlusInt extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asInt(v1) + asInt(v2) }
-}
-case object MinusInt extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asInt(v1) - asInt(v2) }
-}
-case object TimesInt extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asInt(v1) * asInt(v2) }
-}
-case object DivInt extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { DivDecimal.operate(v1,
v2) }
-}
-case object IDivInt extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asInt(DivInt.operate(v1,
v2)) }
-}
-case object ModInt extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asInt(v1) % asInt(v2) }
-}
-
-case object PlusUnsignedShort extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asInt(v1) + asInt(v2) }
-}
-case object MinusUnsignedShort extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asInt(v1) - asInt(v2) }
-}
-case object TimesUnsignedShort extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asInt(v1) * asInt(v2) }
-}
-case object DivUnsignedShort extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { DivDecimal.operate(v1,
v2) }
-}
-case object IDivUnsignedShort extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = {
asInt(DivUnsignedShort.operate(v1, v2)) }
-}
-case object ModUnsignedShort extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asInt(v1) % asInt(v2) }
-}
-
-case object PlusShort extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asShort(v1) + asShort(v2)
}
-}
-case object MinusShort extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asShort(v1) - asShort(v2)
}
-}
-case object TimesShort extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asShort(v1) * asShort(v2)
}
-}
-case object DivShort extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { DivDecimal.operate(v1,
v2) }
-}
-case object IDivShort extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = {
asShort(DivShort.operate(v1, v2)) }
-}
-case object ModShort extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asShort(v1) % asShort(v2)
}
-}
-
-case object PlusUnsignedByte extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asShort(v1) + asShort(v2)
}
-}
-case object MinusUnsignedByte extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asShort(v1) - asShort(v2)
}
-}
-case object TimesUnsignedByte extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asShort(v1) * asShort(v2)
}
-}
-case object DivUnsignedByte extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { DivDecimal.operate(v1,
v2) }
-}
-case object IDivUnsignedByte extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = {
asShort(DivUnsignedByte.operate(v1, v2)) }
-}
-case object ModUnsignedByte extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asShort(v1) % asShort(v2)
}
-}
-
-case object PlusByte extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asByte(v1) + asByte(v2) }
-}
-case object MinusByte extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asByte(v1) - asByte(v2) }
-}
-case object TimesByte extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asByte(v1) * asByte(v2) }
-}
-case object DivByte extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { DivDecimal.operate(v1,
v2) }
-}
-case object IDivByte extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = {
asByte(DivByte.operate(v1, v2)) }
-}
-case object ModByte extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asByte(v1) % asByte(v2) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueLong =
{
+ v1.getLong % v2.getLong
+ }
}
case object PlusFloat extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asFloat(v1) + asFloat(v2)
}
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueFloat
= {
+ v1.getFloat + v2.getFloat
+ }
}
case object MinusFloat extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asFloat(v1) - asFloat(v2)
}
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueFloat
= {
+ v1.getFloat - v2.getFloat
+ }
}
case object TimesFloat extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asFloat(v1) * asFloat(v2)
}
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueFloat
= {
+ v1.getFloat * v2.getFloat
+ }
}
case object DivFloat extends NumericOp {
// div float allows divide by zero to make infinity
- def operate(v1: JNumber, v2: JNumber): JNumber = { asFloat(v1) / asFloat(v2)
}
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueFloat
= {
+ v1.getFloat / v2.getFloat
+ }
}
case object IDivFloat extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = {
- val v1f = asFloat(v1)
- val v2f = asFloat(v2)
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueLong =
{
+ val v1f = v1.getFloat
+ val v2f = v2.getFloat
if (v2f == 0) {
throw new ArithmeticException("/ by zero")
}
if (v1f.isInfinite || v1f.isNaN || v2f.isNaN) {
throw new ArithmeticException("integer division with NaN or Infinity")
}
- asInt(DivFloat.operate(v1, v2))
+ DivFloat.operate(v1, v2).getFloat.toLong
}
}
case object ModFloat extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asFloat(v1) % asFloat(v2)
}
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueFloat
= {
+ v1.getFloat % v2.getFloat
+ }
}
case object PlusDouble extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asDouble(v1) +
asDouble(v2) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueDouble
= {
+ v1.getDouble + v2.getDouble
+ }
}
case object MinusDouble extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asDouble(v1) -
asDouble(v2) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueDouble
= {
+ v1.getDouble - v2.getDouble
+ }
}
case object TimesDouble extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asDouble(v1) *
asDouble(v2) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueDouble
= {
+ v1.getDouble * v2.getDouble
+ }
}
case object DivDouble extends NumericOp {
// div double allows divide by zero to make infinity
- def operate(v1: JNumber, v2: JNumber): JNumber = { asDouble(v1) /
asDouble(v2) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueDouble
= {
+ v1.getDouble / v2.getDouble
+ }
}
case object IDivDouble extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = {
- val v1d = asDouble(v1)
- val v2d = asDouble(v2)
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueLong =
{
+ val v1d = v1.getDouble
+ val v2d = v2.getDouble
if (v2d == 0) {
throw new ArithmeticException("/ by zero")
}
if (v1d.isInfinite || v1d.isNaN || v2d.isNaN) {
throw new ArithmeticException("integer division with NaN or Infinity")
}
- asLong(DivDouble.operate(v1, v2))
+ DivDouble.operate(v1, v2).getDouble.toLong
}
}
case object ModDouble extends NumericOp {
- def operate(v1: JNumber, v2: JNumber): JNumber = { asDouble(v1) %
asDouble(v2) }
+ def operate(v1: DataValuePrimitive, v2: DataValuePrimitive): DataValueDouble
= {
+ v1.getDouble % v2.getDouble
+ }
}
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/expressions.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/expressions.tdml
index 81c1f394e..7301e4164 100644
---
a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/expressions.tdml
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/expressions.tdml
@@ -7390,7 +7390,7 @@ blastoff
<xs:element name="div20" type="xs:float" dfdl:inputValueCalc="{
xs:float(5) div xs:float('INF') }" />
<xs:element name="div21" type="xs:double" dfdl:inputValueCalc="{ 90.0 div
1234567.0 }" />
<xs:element name="div22" type="xs:decimal" dfdl:inputValueCalc="{ (9 *
xs:long(5)) div 1600 }" />
- <xs:element name="div23" type="xs:double" dfdl:inputValueCalc="{ (9 *
xs:long(5)) div 1600 }" />
+ <xs:element name="div23" type="xs:double" dfdl:inputValueCalc="{ (9 *
xs:decimal(5)) div 1600 }" />
<xs:element name="div24" type="xs:float" dfdl:inputValueCalc="{
(xs:int(5)) div xs:int(3) }" />
<xs:element name="idiv01" type="xs:int" dfdl:inputValueCalc="{
xs:double(5) idiv xs:double(2) }" />
@@ -7415,6 +7415,18 @@ blastoff
<xs:element name="idiv20" type="xs:int" dfdl:inputValueCalc="{ xs:float(5)
idiv xs:float('INF') }" />
<xs:element name="add01" type="xs:int" dfdl:inputValueCalc="{
xs:unsignedInt(5) + (-1) }" />
+ <xs:element name="add02" type="xs:unsignedByte" dfdl:inputValueCalc="{
xs:unsignedByte(5) * xs:unsignedByte(2) }" />
+ <xs:element name="add03" type="xs:unsignedByte" dfdl:inputValueCalc="{
xs:unsignedByte(255) + xs:unsignedByte(1) }" />
+ <xs:element name="add04" type="xs:unsignedByte" dfdl:inputValueCalc="{
xs:unsignedByte(255) + xs:unsignedByte(1) - xs:unsignedByte(1) }" />
+
+ <xs:element name="mul01" type="xs:long" dfdl:inputValueCalc="{
xs:unsignedByte(5) * xs:unsignedByte(3) }" />
+ <xs:element name="mul02" type="xs:decimal" dfdl:inputValueCalc="{
xs:decimal(5.0) * xs:decimal(3) }" />
+ <xs:element name="mul03" type="xs:double" dfdl:inputValueCalc="{
xs:double(5) * xs:double(3) }" />
+
+ <xs:element name="mod01" type="xs:long" dfdl:inputValueCalc="{ xs:int(10)
mod xs:int(3) }" />
+ <xs:element name="mod02" type="xs:double" dfdl:inputValueCalc="{
xs:double(10.5) mod xs:double(3) }" />
+ <xs:element name="mod03" type="xs:float" dfdl:inputValueCalc="{
xs:float(10.5) mod xs:float(3) }" />
+ <xs:element name="mod04" type="xs:decimal" dfdl:inputValueCalc="{
xs:decimal(10.5) mod xs:decimal(3) }" />
</tdml:defineSchema>
<tdml:parserTestCase name="div01" root="div01" model="XPathMath"
description="Section 23 - DFDL Expressions - div">
@@ -7561,7 +7573,7 @@ blastoff
<tdml:document></tdml:document>
<tdml:errors>
<tdml:error>result type</tdml:error>
- <tdml:error>Decimal</tdml:error>
+ <tdml:error>Double</tdml:error>
<tdml:error>must be manually cast</tdml:error>
<tdml:error>Float</tdml:error>
</tdml:errors>
@@ -7702,6 +7714,62 @@ blastoff
<tdml:infoset><tdml:dfdlInfoset><add01>4</add01></tdml:dfdlInfoset></tdml:infoset>
</tdml:parserTestCase>
+ <tdml:parserTestCase name="add02" root="add02" model="XPathMath"
description="Section 23 - DFDL Expressions - div">
+ <tdml:document></tdml:document>
+
<tdml:infoset><tdml:dfdlInfoset><add02>10</add02></tdml:dfdlInfoset></tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="add03" root="add03" model="XPathMath"
description="Section 23 - DFDL Expressions - div">
+ <tdml:document></tdml:document>
+ <tdml:errors>
+ <tdml:error>Schema Definition Error</tdml:error>
+ <tdml:error>Cannot convert</tdml:error>
+ <tdml:error>256</tdml:error>
+ <tdml:error>out of range</tdml:error>
+ <tdml:error>unsignedByte</tdml:error>
+ </tdml:errors>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="add04" root="add04" model="XPathMath"
description="Section 23 - DFDL Expressions - div">
+ <tdml:document></tdml:document>
+
<tdml:infoset><tdml:dfdlInfoset><add04>255</add04></tdml:dfdlInfoset></tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="mul01" root="mul01" model="XPathMath"
description="Section 23 - DFDL Expressions - div">
+ <tdml:document></tdml:document>
+
<tdml:infoset><tdml:dfdlInfoset><mul01>15</mul01></tdml:dfdlInfoset></tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="mul02" root="mul02" model="XPathMath"
description="Section 23 - DFDL Expressions - div">
+ <tdml:document></tdml:document>
+
<tdml:infoset><tdml:dfdlInfoset><mul02>15.0</mul02></tdml:dfdlInfoset></tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="mul03" root="mul03" model="XPathMath"
description="Section 23 - DFDL Expressions - div">
+ <tdml:document></tdml:document>
+
<tdml:infoset><tdml:dfdlInfoset><mul03>15.0</mul03></tdml:dfdlInfoset></tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="mod01" root="mod01" model="XPathMath"
description="Section 23 - DFDL Expressions - div">
+ <tdml:document></tdml:document>
+
<tdml:infoset><tdml:dfdlInfoset><mod01>1</mod01></tdml:dfdlInfoset></tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="mod02" root="mod02" model="XPathMath"
description="Section 23 - DFDL Expressions - div">
+ <tdml:document></tdml:document>
+
<tdml:infoset><tdml:dfdlInfoset><mod02>1.5</mod02></tdml:dfdlInfoset></tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="mod03" root="mod03" model="XPathMath"
description="Section 23 - DFDL Expressions - div">
+ <tdml:document></tdml:document>
+
<tdml:infoset><tdml:dfdlInfoset><mod03>1.5</mod03></tdml:dfdlInfoset></tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:parserTestCase name="mod04" root="mod04" model="XPathMath"
description="Section 23 - DFDL Expressions - div">
+ <tdml:document></tdml:document>
+
<tdml:infoset><tdml:dfdlInfoset><mod04>1.5</mod04></tdml:dfdlInfoset></tdml:infoset>
+ </tdml:parserTestCase>
+
<tdml:defineSchema name="DFDLCheckRange">
<xs:include
schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
<dfdl:format ref="ex:GeneralFormat" />
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/expressions2.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/expressions2.tdml
index 7337a8cba..1602028d6 100644
---
a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/expressions2.tdml
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/expressions2.tdml
@@ -218,7 +218,7 @@
<tdml:errors>
<tdml:error>Schema Definition Error</tdml:error>
<tdml:error>If-expression branches must have similar types</tdml:error>
- <tdml:error>Double</tdml:error>
+ <tdml:error>Decimal</tdml:error>
<tdml:error>String</tdml:error>
</tdml:errors>
</tdml:parserTestCase>
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/functions.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/functions.tdml
index c9f274999..b5d2ec2aa 100644
---
a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/functions.tdml
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_expressions/functions.tdml
@@ -316,11 +316,11 @@
<xs:element name="substring04" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', 0, 3) }"/>
<xs:element name="substring05" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', 5, -3) }"/>
<xs:element name="substring06" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', -3, 5) }"/>
- <xs:element name="substring07" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', 0 div 0E0, 3) }"/>
- <xs:element name="substring08" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', 1, 0 div 0E0) }"/>
- <xs:element name="substring12" type="xs:string" dfdl:inputValueCalc="{
fn:substring('motor car', 1 div 0E0) }"/>
- <xs:element name="substring13" type="xs:string" dfdl:inputValueCalc="{
fn:substring('', 1 div 0E0) }"/>
- <xs:element name="substring14" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', -1 div 0E0) }"/>
+ <xs:element name="substring07" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', 0.0 div 0E0, 3) }"/>
+ <xs:element name="substring08" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', 1, 0.0 div 0E0) }"/>
+ <xs:element name="substring12" type="xs:string" dfdl:inputValueCalc="{
fn:substring('motor car', 1.0 div 0E0) }"/>
+ <xs:element name="substring13" type="xs:string" dfdl:inputValueCalc="{
fn:substring('', 1.0 div 0E0) }"/>
+ <xs:element name="substring14" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', -1.0 div 0E0) }"/>
<xs:element name="substring15" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', -10, 1) }"/>
</tdml:defineSchema>
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml
index a9a8679a6..28d26da5a 100644
---
a/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml
@@ -600,8 +600,8 @@
</xs:complexType>
</xs:element>
<xs:element name="e_substring8" type="xs:string" dfdl:inputValueCalc="{
fn:substring('', 1, 2) }" />
- <xs:element name="e_substring9" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', -42, 1 div 0E0) }" />
- <xs:element name="e_substring10" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', -1 div 0E0, 1 div 0E0) }" />
+ <xs:element name="e_substring9" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', -42, 1.0 div 0E0) }" />
+ <xs:element name="e_substring10" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', -1.0 div 0E0, 1.0 div 0E0) }" />
<xs:element name="e_substring11" type="xs:string" dfdl:inputValueCalc="{
fn:substring('12345', -3, 5) }" />
<xs:element name="e_stringlength1" type="xs:int" dfdl:inputValueCalc="{
fn:string-length('string') }" />
diff --git
a/daffodil-test/src/test/scala/org/apache/daffodil/section23/dfdl_expressions/TestDFDLExpressions.scala
b/daffodil-test/src/test/scala/org/apache/daffodil/section23/dfdl_expressions/TestDFDLExpressions.scala
index bfd17691c..8459131f0 100644
---
a/daffodil-test/src/test/scala/org/apache/daffodil/section23/dfdl_expressions/TestDFDLExpressions.scala
+++
b/daffodil-test/src/test/scala/org/apache/daffodil/section23/dfdl_expressions/TestDFDLExpressions.scala
@@ -404,6 +404,18 @@ class TestDFDLExpressions extends TdmlTests {
@Test def hexBinaryComparison_05 = test
@Test def add01 = test
+ @Test def add02 = test
+ @Test def add03 = test
+ @Test def add04 = test
+
+ @Test def mul01 = test
+ @Test def mul02 = test
+ @Test def mul03 = test
+
+ @Test def mod01 = test
+ @Test def mod02 = test
+ @Test def mod03 = test
+ @Test def mod04 = test
// DFDL-1617 - should detect errors due to query-style expressions
@Test def query_style_01 = test