You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by sl...@apache.org on 2023/06/07 18:29:19 UTC

[daffodil] branch main updated: Support text numbers that parse to negative zero double

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 0299a828f Support text numbers that parse to negative zero double
0299a828f is described below

commit 0299a828fc6f52abaf36961b92f0c03cb89bb6b8
Author: Steve Lawrence <sl...@apache.org>
AuthorDate: Wed Jun 7 12:34:14 2023 -0400

    Support text numbers that parse to negative zero double
    
    ICU parses negative zero to a Double type, which we do not currently
    allow, expecting only Infinifity or NaN. This modifies the assertion to
    allow negative zero as well.
    
    DAFFODIL-2818
---
 .../parsers/ConvertTextStandardNumberParser.scala  | 17 ++++++++-----
 .../runtime1/processors/input/TestICU.scala        | 29 ++++++++++++++++++++++
 .../section05/simple_types/SimpleTypes.tdml        | 12 +++++++++
 .../section05/simple_types/TestSimpleTypes.scala   |  1 +
 4 files changed, 53 insertions(+), 6 deletions(-)

diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/ConvertTextStandardNumberParser.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/ConvertTextStandardNumberParser.scala
index e5c05a5ca..29513c6f4 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/ConvertTextStandardNumberParser.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/ConvertTextStandardNumberParser.scala
@@ -81,8 +81,11 @@ trait TextDecimalVirtualPointMixin {
           bd.scaleByPowerOfTen(-textDecimalVirtualPoint)
         }
         case d: JDouble => {
-          // ICU only returns doubles if they are infinity/NaN, these cannot be scaled
-          Assert.invariant(d.isNaN || d.isInfinite)
+          // ICU only returns doubles if they are -INF, INF, NaN, or negative zero, which do not
+          // need scaling
+          Assert.invariant(
+            d.isNaN || d.isInfinite || JDouble.doubleToLongBits(d) == 0x8000000000000000L,
+          )
           d
         }
         // $COVERAGE-OFF$
@@ -228,10 +231,12 @@ case class ConvertTextStandardNumberParser(
             }
           }
           case d: JDouble => {
-            // ICU returns a Double only if it parsed NaN, Infinity, or -Infinity. We will later
-            // pass this value in primNumber.fromNumber, which will fail if the primitive type
-            // does not allow NaN/Infinity
-            Assert.invariant(d.isNaN || d.isInfinite)
+            // ICU returns a Double only if it parsed NaN, Infinity, -Infinity, or negative
+            // zero. We will later pass this value in primNumber.fromNumber, which will fail if
+            // the primitive type does not allow NaN/Infinity
+            Assert.invariant(
+              d.isNaN || d.isInfinite || JDouble.doubleToLongBits(d) == 0x8000000000000000L,
+            )
             d
           }
           case bd: ICUBigDecimal => {
diff --git a/daffodil-runtime1/src/test/scala/org/apache/daffodil/runtime1/processors/input/TestICU.scala b/daffodil-runtime1/src/test/scala/org/apache/daffodil/runtime1/processors/input/TestICU.scala
index 647404c71..fdb5d3aca 100644
--- a/daffodil-runtime1/src/test/scala/org/apache/daffodil/runtime1/processors/input/TestICU.scala
+++ b/daffodil-runtime1/src/test/scala/org/apache/daffodil/runtime1/processors/input/TestICU.scala
@@ -23,6 +23,7 @@ import java.text.ParsePosition
 
 import org.apache.daffodil.lib.calendar.TextCalendarConstants
 
+import com.ibm.icu.math.{ BigDecimal => ICUBigDecimal }
 import com.ibm.icu.text.DecimalFormat
 import com.ibm.icu.text.DecimalFormatSymbols
 import com.ibm.icu.text.SimpleDateFormat
@@ -276,4 +277,32 @@ class TestICU {
     assertEquals(dfs.getNaN, df.format(JDouble.NaN))
   }
 
+  /**
+   * Regardless of the ICU number pattern or text being parsed, ICU always returns the following
+   * types
+   *
+   * - Double if the parsed value is INF, -INF, NaN, or negative zero
+   * - Long if the parsed value has no fractional parts and fits in a long
+   * - ICUBigDecimal otherwise
+   */
+  @Test def test_floatingPointReturnType(): Unit = {
+    val dfs = new DecimalFormatSymbols()
+    val df = new DecimalFormat("", dfs)
+    df.applyPattern("###0.0##;-###0.0##")
+
+    assertTrue(df.parse("1.0", pp).isInstanceOf[Long])
+    assertTrue(df.parse("-1.0", pp).isInstanceOf[Long])
+    assertTrue(df.parse("0.0", pp).isInstanceOf[Long])
+    assertTrue(df.parse("9223372036854775807", pp).isInstanceOf[Long])
+
+    assertTrue(df.parse(dfs.getNaN, pp).isInstanceOf[Double])
+    assertTrue(df.parse(dfs.getInfinity, pp).isInstanceOf[Double])
+    assertTrue(df.parse("-" + dfs.getInfinity, pp).isInstanceOf[Double])
+    assertTrue(df.parse("-0.0", pp).isInstanceOf[Double])
+
+    assertTrue(df.parse("9223372036854775808", pp).isInstanceOf[ICUBigDecimal])
+    assertTrue(df.parse("122.75", pp).isInstanceOf[ICUBigDecimal])
+    assertTrue(df.parse("0.3", pp).isInstanceOf[ICUBigDecimal])
+  }
+
 }
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section05/simple_types/SimpleTypes.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section05/simple_types/SimpleTypes.tdml
index ee7b5bcd5..68921b3df 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section05/simple_types/SimpleTypes.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section05/simple_types/SimpleTypes.tdml
@@ -3683,6 +3683,18 @@
     </tdml:errors>
   </tdml:unparserTestCase>
 
+  <tdml:parserTestCase name="double_negativeZero" root="doubleText"
+    model="SimpleTypes-Embedded.dfdl.xsd" description="Section 5 Schema types-double - DFDL-5-009R"
+    roundTrip="false">
+
+    <tdml:document><![CDATA[-0.0]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <doubleText>-0.0</doubleText>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
   <tdml:parserTestCase name="float_text" root="floatText"
     model="SimpleTypes-Embedded.dfdl.xsd" description="Section 5 Simple type-float - DFDL-5-008R"
     roundTrip="false">
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/section05/simple_types/TestSimpleTypes.scala b/daffodil-test/src/test/scala/org/apache/daffodil/section05/simple_types/TestSimpleTypes.scala
index 42fe7f94d..63cb1cdcf 100644
--- a/daffodil-test/src/test/scala/org/apache/daffodil/section05/simple_types/TestSimpleTypes.scala
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/section05/simple_types/TestSimpleTypes.scala
@@ -821,6 +821,7 @@ class TestSimpleTypes {
     runner.runOneTest("characterDuringValidDouble")
   }
   @Test def test_double_unparseError(): Unit = { runner.runOneTest("double_unparseError") }
+  @Test def test_double_negativeZero(): Unit = { runner.runOneTest("double_negativeZero") }
 
   @Test def test_float_text(): Unit = { runner.runOneTest("float_text") }
   @Test def test_float_text2(): Unit = { runner.runOneTest("float_text2") }