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


Reply via email to