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");
}