This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-math.git


The following commit(s) were added to refs/heads/master by this push:
     new 30356b215 Trap out-of-range exponents in the Dfp string constructor 
(#341)
30356b215 is described below

commit 30356b2153b3a4fd9ecb2f64a2a6e988576798b2
Author: insaf021 <[email protected]>
AuthorDate: Wed Oct 28 13:58:15 2026 +0530

    Trap out-of-range exponents in the Dfp string constructor (#341)
    
    Dfp(DfpField, String) discarded the flag returned by round(), so an
    exponent above MAX_EXP or below the gradual underflow range produced a
    finite value that every arithmetic operation would have trapped to
    infinity or zero. The exponent digits were also accumulated in an int
    without a bound, so "1e2147483648" wrapped negative and toString() on
    the result looped forever in dfp2sci.
    
    Stop accumulating exponent digits once they are past any representable
    magnitude, store a zero mantissa with a zero exponent, and route the
    round() flag through dotrap as the arithmetic operations do.
    
    The "Next After #14" literal 9.99e131078 in DfpTest and DfpDecTest is
    beyond the largest finite value and only parsed as finite because of
    this bug; it is corrected to e131071.
---
 .../apache/commons/math4/legacy/core/dfp/Dfp.java  | 25 +++++++++++-
 .../commons/math4/legacy/core/dfp/DfpDecTest.java  |  2 +-
 .../commons/math4/legacy/core/dfp/DfpTest.java     | 44 +++++++++++++++++++++-
 3 files changed, 67 insertions(+), 4 deletions(-)

diff --git 
a/commons-math-legacy-core/src/main/java/org/apache/commons/math4/legacy/core/dfp/Dfp.java
 
b/commons-math-legacy-core/src/main/java/org/apache/commons/math4/legacy/core/dfp/Dfp.java
index f960635ae..700aa399e 100644
--- 
a/commons-math-legacy-core/src/main/java/org/apache/commons/math4/legacy/core/dfp/Dfp.java
+++ 
b/commons-math-legacy-core/src/main/java/org/apache/commons/math4/legacy/core/dfp/Dfp.java
@@ -160,6 +160,12 @@ public class Dfp implements RealFieldElement<Dfp> {
     /** Name for traps triggered by newInstance. */
     private static final String NEW_INSTANCE_TRAP = "newInstance";
 
+    /** Largest decimal exponent accumulated when parsing a string.
+     * Any exponent beyond it overflows or underflows whatever the remaining
+     * digits are, and the bound leaves room for one more digit and for the
+     * position of the decimal point without overflowing an int. */
+    private static final int MAX_PARSED_EXP = Integer.MAX_VALUE / 16;
+
     /** Mantissa. */
     protected int[] mant;
 
@@ -382,7 +388,8 @@ public class Dfp implements RealFieldElement<Dfp> {
                     negative = true;
                     continue;
                 }
-                if (fpexp.charAt(i) >= '0' && fpexp.charAt(i) <= '9') {
+                if (fpexp.charAt(i) >= '0' && fpexp.charAt(i) <= '9' &&
+                    sciexp < MAX_PARSED_EXP) {
                     sciexp = sciexp * 10 + fpexp.charAt(i) - '0';
                 }
             }
@@ -522,9 +529,23 @@ public class Dfp implements RealFieldElement<Dfp> {
 
         exp = (decimalPos + sciexp) / rsize;
 
+        // zero is stored with a zero exponent, as the arithmetic does
+        if (mant[mant.length - 1] == 0) {
+            exp = 0;
+        }
+
         if (q < striped.length) {
             // Is there possible another digit?
-            round((striped[q] - '0') * 1000);
+            final int excp = round((striped[q] - '0') * 1000);
+            if (excp != 0) {
+                // apply the overflow or underflow handling that the
+                // arithmetic operations get from dotrap
+                final Dfp result = dotrap(excp, NEW_INSTANCE_TRAP, null, this);
+                System.arraycopy(result.mant, 0, mant, 0, mant.length);
+                sign = result.sign;
+                exp  = result.exp;
+                nans = result.nans;
+            }
         }
     }
 
diff --git 
a/commons-math-legacy-core/src/test/java/org/apache/commons/math4/legacy/core/dfp/DfpDecTest.java
 
b/commons-math-legacy-core/src/test/java/org/apache/commons/math4/legacy/core/dfp/DfpDecTest.java
index 6e471f038..4e8d637ee 100644
--- 
a/commons-math-legacy-core/src/test/java/org/apache/commons/math4/legacy/core/dfp/DfpDecTest.java
+++ 
b/commons-math-legacy-core/src/test/java/org/apache/commons/math4/legacy/core/dfp/DfpDecTest.java
@@ -558,7 +558,7 @@ public class DfpDecTest {
              new DfpDec(field, "0"),
              DfpField.FLAG_UNDERFLOW | DfpField.FLAG_INEXACT, "Next After 
#13");
 
-        test(new DfpDec(field, "9.9999999999999999e131078").nextAfter(pinf),
+        test(new DfpDec(field, "9.9999999999999999e131071").nextAfter(pinf),
              pinf,
              DfpField.FLAG_OVERFLOW | DfpField.FLAG_INEXACT, "Next After #14");
     }
diff --git 
a/commons-math-legacy-core/src/test/java/org/apache/commons/math4/legacy/core/dfp/DfpTest.java
 
b/commons-math-legacy-core/src/test/java/org/apache/commons/math4/legacy/core/dfp/DfpTest.java
index 19469e439..345d3efdc 100644
--- 
a/commons-math-legacy-core/src/test/java/org/apache/commons/math4/legacy/core/dfp/DfpTest.java
+++ 
b/commons-math-legacy-core/src/test/java/org/apache/commons/math4/legacy/core/dfp/DfpTest.java
@@ -119,6 +119,48 @@ public class DfpTest extends 
ExtendedFieldElementAbstractTest<Dfp> {
     /*
      *  Test addition
      */
+    @Test
+    public void testStringConstructorExponentRange() {
+        // exponent beyond MAX_EXP: an arithmetic result would be trapped to 
infinity
+        Dfp x = field.newDfp("1e999999");
+        Assert.assertTrue(x.isInfinite());
+        Assert.assertEquals(1, x.sign);
+        Assert.assertEquals(DfpField.FLAG_OVERFLOW, field.getIEEEFlags());
+        field.clearIEEEFlags();
+
+        x = field.newDfp("-1e999999");
+        Assert.assertTrue(x.isInfinite());
+        Assert.assertEquals(-1, x.sign);
+        Assert.assertEquals(DfpField.FLAG_OVERFLOW, field.getIEEEFlags());
+        field.clearIEEEFlags();
+
+        // exponent below the gradual underflow range: flushed to zero
+        x = field.newDfp("1e-999999");
+        Assert.assertTrue(x.isZero());
+        Assert.assertEquals(DfpField.FLAG_UNDERFLOW, field.getIEEEFlags());
+        field.clearIEEEFlags();
+
+        // exponent digits that do not fit in an int must not wrap around
+        x = field.newDfp("1e2147483648");
+        Assert.assertTrue(x.isInfinite());
+        Assert.assertEquals("Infinity", x.toString());
+        
Assert.assertTrue(field.newDfp("-1e99999999999999999999").isInfinite());
+        field.clearIEEEFlags();
+
+        // a zero mantissa is zero whatever the exponent says
+        x = field.newDfp("0e999999");
+        Assert.assertTrue(x.isZero());
+        Assert.assertEquals(0, field.getIEEEFlags());
+
+        // the largest normal and gradual underflow values are unchanged
+        x = field.newDfp("9.9999999999999999999e131071");
+        Assert.assertFalse(x.isInfinite());
+        Assert.assertEquals(0, field.getIEEEFlags());
+        test(field.newDfp("1e-131078"),
+             field.newDfp("1e-131072").divide(field.newDfp("1e6")),
+             DfpField.FLAG_UNDERFLOW, "String #1");
+    }
+
     @Test
     public void testAdd() {
         test(field.newDfp("1").add(field.newDfp("1")),      // Basic tests   
1+1 = 2
@@ -1230,7 +1272,7 @@ public class DfpTest extends 
ExtendedFieldElementAbstractTest<Dfp> {
              field.newDfp("0"),
              DfpField.FLAG_UNDERFLOW | DfpField.FLAG_INEXACT, "Next After 
#13");
 
-        test(field.newDfp("9.9999999999999999999e131078").nextAfter(pinf),
+        test(field.newDfp("9.9999999999999999999e131071").nextAfter(pinf),
              pinf,
              DfpField.FLAG_OVERFLOW | DfpField.FLAG_INEXACT, "Next After #14");
     }

Reply via email to