mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8233760: Result of BigDecimal.toString throws overflow exception on new BigDecimal(str)
Reviewed-by: darcy
This commit is contained in:
parent
b92ce2699b
commit
c15e10fb6c
2 changed files with 44 additions and 47 deletions
|
@ -361,7 +361,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
private static final long serialVersionUID = 6108874887143696463L;
|
private static final long serialVersionUID = 6108874887143696463L;
|
||||||
|
|
||||||
// Cache of common small BigDecimal values.
|
// Cache of common small BigDecimal values.
|
||||||
private static final BigDecimal ZERO_THROUGH_TEN[] = {
|
private static final BigDecimal[] ZERO_THROUGH_TEN = {
|
||||||
new BigDecimal(BigInteger.ZERO, 0, 0, 1),
|
new BigDecimal(BigInteger.ZERO, 0, 0, 1),
|
||||||
new BigDecimal(BigInteger.ONE, 1, 0, 1),
|
new BigDecimal(BigInteger.ONE, 1, 0, 1),
|
||||||
new BigDecimal(BigInteger.TWO, 2, 0, 1),
|
new BigDecimal(BigInteger.TWO, 2, 0, 1),
|
||||||
|
@ -516,7 +516,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
|
|
||||||
// Use locals for all fields values until completion
|
// Use locals for all fields values until completion
|
||||||
int prec = 0; // record precision value
|
int prec = 0; // record precision value
|
||||||
int scl = 0; // record scale value
|
long scl = 0; // record scale value
|
||||||
long rs = 0; // the compact value in long
|
long rs = 0; // the compact value in long
|
||||||
BigInteger rb = null; // the inflated value in BigInteger
|
BigInteger rb = null; // the inflated value in BigInteger
|
||||||
// use array bounds checking to handle too-long, len == 0,
|
// use array bounds checking to handle too-long, len == 0,
|
||||||
|
@ -535,7 +535,6 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
|
|
||||||
// should now be at numeric part of the significand
|
// should now be at numeric part of the significand
|
||||||
boolean dot = false; // true when there is a '.'
|
boolean dot = false; // true when there is a '.'
|
||||||
long exp = 0; // exponent
|
|
||||||
char c; // current character
|
char c; // current character
|
||||||
boolean isCompact = (len <= MAX_COMPACT_DIGITS);
|
boolean isCompact = (len <= MAX_COMPACT_DIGITS);
|
||||||
// integer significand array & idx is the index to it. The array
|
// integer significand array & idx is the index to it. The array
|
||||||
|
@ -585,10 +584,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
if (dot)
|
if (dot)
|
||||||
++scl;
|
++scl;
|
||||||
} else if ((c == 'e') || (c == 'E')) {
|
} else if ((c == 'e') || (c == 'E')) {
|
||||||
exp = parseExp(in, offset, len);
|
scl -= parseExp(in, offset, len);
|
||||||
// Next test is required for backwards compatibility
|
|
||||||
if ((int) exp != exp) // overflow
|
|
||||||
throw new NumberFormatException("Exponent overflow.");
|
|
||||||
break; // [saves a test]
|
break; // [saves a test]
|
||||||
} else {
|
} else {
|
||||||
throw new NumberFormatException("Character " + c
|
throw new NumberFormatException("Character " + c
|
||||||
|
@ -598,24 +594,20 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
}
|
}
|
||||||
if (prec == 0) // no digits found
|
if (prec == 0) // no digits found
|
||||||
throw new NumberFormatException("No digits found.");
|
throw new NumberFormatException("No digits found.");
|
||||||
// Adjust scale if exp is not zero.
|
|
||||||
if (exp != 0) { // had significant exponent
|
|
||||||
scl = adjustScale(scl, exp);
|
|
||||||
}
|
|
||||||
rs = isneg ? -rs : rs;
|
rs = isneg ? -rs : rs;
|
||||||
int mcp = mc.precision;
|
int mcp = mc.precision;
|
||||||
int drop = prec - mcp; // prec has range [1, MAX_INT], mcp has range [0, MAX_INT];
|
int drop = prec - mcp; // prec has range [1, MAX_INT], mcp has range [0, MAX_INT];
|
||||||
// therefore, this subtract cannot overflow
|
// therefore, this subtraction cannot overflow
|
||||||
if (mcp > 0 && drop > 0) { // do rounding
|
if (mcp > 0 && drop > 0) { // do rounding
|
||||||
while (drop > 0) {
|
while (drop > 0) {
|
||||||
scl = checkScaleNonZero((long) scl - drop);
|
scl -= drop;
|
||||||
rs = divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
|
rs = divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
|
||||||
prec = longDigitLength(rs);
|
prec = longDigitLength(rs);
|
||||||
drop = prec - mcp;
|
drop = prec - mcp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
char coeff[] = new char[len];
|
char[] coeff = new char[len];
|
||||||
for (; len > 0; offset++, len--) {
|
for (; len > 0; offset++, len--) {
|
||||||
c = in[offset];
|
c = in[offset];
|
||||||
// have digit
|
// have digit
|
||||||
|
@ -652,19 +644,12 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
if ((c != 'e') && (c != 'E'))
|
if ((c != 'e') && (c != 'E'))
|
||||||
throw new NumberFormatException("Character array"
|
throw new NumberFormatException("Character array"
|
||||||
+ " is missing \"e\" notation exponential mark.");
|
+ " is missing \"e\" notation exponential mark.");
|
||||||
exp = parseExp(in, offset, len);
|
scl -= parseExp(in, offset, len);
|
||||||
// Next test is required for backwards compatibility
|
|
||||||
if ((int) exp != exp) // overflow
|
|
||||||
throw new NumberFormatException("Exponent overflow.");
|
|
||||||
break; // [saves a test]
|
break; // [saves a test]
|
||||||
}
|
}
|
||||||
// here when no characters left
|
// here when no characters left
|
||||||
if (prec == 0) // no digits found
|
if (prec == 0) // no digits found
|
||||||
throw new NumberFormatException("No digits found.");
|
throw new NumberFormatException("No digits found.");
|
||||||
// Adjust scale if exp is not zero.
|
|
||||||
if (exp != 0) { // had significant exponent
|
|
||||||
scl = adjustScale(scl, exp);
|
|
||||||
}
|
|
||||||
// Remove leading zeros from precision (digits count)
|
// Remove leading zeros from precision (digits count)
|
||||||
rb = new BigInteger(coeff, isneg ? -1 : 1, prec);
|
rb = new BigInteger(coeff, isneg ? -1 : 1, prec);
|
||||||
rs = compactValFor(rb);
|
rs = compactValFor(rb);
|
||||||
|
@ -673,7 +658,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
if (rs == INFLATED) {
|
if (rs == INFLATED) {
|
||||||
int drop = prec - mcp;
|
int drop = prec - mcp;
|
||||||
while (drop > 0) {
|
while (drop > 0) {
|
||||||
scl = checkScaleNonZero((long) scl - drop);
|
scl -= drop;
|
||||||
rb = divideAndRoundByTenPow(rb, drop, mc.roundingMode.oldMode);
|
rb = divideAndRoundByTenPow(rb, drop, mc.roundingMode.oldMode);
|
||||||
rs = compactValFor(rb);
|
rs = compactValFor(rb);
|
||||||
if (rs != INFLATED) {
|
if (rs != INFLATED) {
|
||||||
|
@ -687,7 +672,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
if (rs != INFLATED) {
|
if (rs != INFLATED) {
|
||||||
int drop = prec - mcp;
|
int drop = prec - mcp;
|
||||||
while (drop > 0) {
|
while (drop > 0) {
|
||||||
scl = checkScaleNonZero((long) scl - drop);
|
scl -= drop;
|
||||||
rs = divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
|
rs = divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
|
||||||
prec = longDigitLength(rs);
|
prec = longDigitLength(rs);
|
||||||
drop = prec - mcp;
|
drop = prec - mcp;
|
||||||
|
@ -701,20 +686,14 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
nfe.initCause(e);
|
nfe.initCause(e);
|
||||||
throw nfe;
|
throw nfe;
|
||||||
}
|
}
|
||||||
this.scale = scl;
|
if ((int) scl != scl) // overflow
|
||||||
|
throw new NumberFormatException("Exponent overflow.");
|
||||||
|
this.scale = (int) scl;
|
||||||
this.precision = prec;
|
this.precision = prec;
|
||||||
this.intCompact = rs;
|
this.intCompact = rs;
|
||||||
this.intVal = rb;
|
this.intVal = rb;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int adjustScale(int scl, long exp) {
|
|
||||||
long adjustedScale = scl - exp;
|
|
||||||
if (adjustedScale > Integer.MAX_VALUE || adjustedScale < Integer.MIN_VALUE)
|
|
||||||
throw new NumberFormatException("Scale out of range.");
|
|
||||||
scl = (int) adjustedScale;
|
|
||||||
return scl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* parse exponent
|
* parse exponent
|
||||||
*/
|
*/
|
||||||
|
@ -819,9 +798,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
*
|
*
|
||||||
* <p>The exponent consists of the character {@code 'e'}
|
* <p>The exponent consists of the character {@code 'e'}
|
||||||
* (<code>'\u0065'</code>) or {@code 'E'} (<code>'\u0045'</code>)
|
* (<code>'\u0065'</code>) or {@code 'E'} (<code>'\u0045'</code>)
|
||||||
* followed by one or more decimal digits. The value of the
|
* followed by one or more decimal digits.
|
||||||
* exponent must lie between -{@link Integer#MAX_VALUE} ({@link
|
|
||||||
* Integer#MIN_VALUE}+1) and {@link Integer#MAX_VALUE}, inclusive.
|
|
||||||
*
|
*
|
||||||
* <p>More formally, the strings this constructor accepts are
|
* <p>More formally, the strings this constructor accepts are
|
||||||
* described by the following grammar:
|
* described by the following grammar:
|
||||||
|
@ -1440,7 +1417,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
|
|
||||||
long padding = (long) lhs.scale - augend.scale;
|
long padding = (long) lhs.scale - augend.scale;
|
||||||
if (padding != 0) { // scales differ; alignment needed
|
if (padding != 0) { // scales differ; alignment needed
|
||||||
BigDecimal arg[] = preAlign(lhs, augend, padding, mc);
|
BigDecimal[] arg = preAlign(lhs, augend, padding, mc);
|
||||||
matchScale(arg);
|
matchScale(arg);
|
||||||
lhs = arg[0];
|
lhs = arg[0];
|
||||||
augend = arg[1];
|
augend = arg[1];
|
||||||
|
@ -1975,7 +1952,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
*/
|
*/
|
||||||
public BigDecimal remainder(BigDecimal divisor) {
|
public BigDecimal remainder(BigDecimal divisor) {
|
||||||
BigDecimal divrem[] = this.divideAndRemainder(divisor);
|
BigDecimal[] divrem = this.divideAndRemainder(divisor);
|
||||||
return divrem[1];
|
return divrem[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2005,7 +1982,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
*/
|
*/
|
||||||
public BigDecimal remainder(BigDecimal divisor, MathContext mc) {
|
public BigDecimal remainder(BigDecimal divisor, MathContext mc) {
|
||||||
BigDecimal divrem[] = this.divideAndRemainder(divisor, mc);
|
BigDecimal[] divrem = this.divideAndRemainder(divisor, mc);
|
||||||
return divrem[1];
|
return divrem[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3831,7 +3808,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
* Powers of 10 which can be represented exactly in {@code
|
* Powers of 10 which can be represented exactly in {@code
|
||||||
* double}.
|
* double}.
|
||||||
*/
|
*/
|
||||||
private static final double DOUBLE_10_POW[] = {
|
private static final double[] DOUBLE_10_POW = {
|
||||||
1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5,
|
1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5,
|
||||||
1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10, 1.0e11,
|
1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10, 1.0e11,
|
||||||
1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17,
|
1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17,
|
||||||
|
@ -3842,7 +3819,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
* Powers of 10 which can be represented exactly in {@code
|
* Powers of 10 which can be represented exactly in {@code
|
||||||
* float}.
|
* float}.
|
||||||
*/
|
*/
|
||||||
private static final float FLOAT_10_POW[] = {
|
private static final float[] FLOAT_10_POW = {
|
||||||
1.0e0f, 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f,
|
1.0e0f, 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f,
|
||||||
1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f
|
1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f
|
||||||
};
|
};
|
||||||
|
@ -4148,7 +4125,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
1000000000000000000L // 18 / 10^18
|
1000000000000000000L // 18 / 10^18
|
||||||
};
|
};
|
||||||
|
|
||||||
private static volatile BigInteger BIG_TEN_POWERS_TABLE[] = {
|
private static volatile BigInteger[] BIG_TEN_POWERS_TABLE = {
|
||||||
BigInteger.ONE,
|
BigInteger.ONE,
|
||||||
BigInteger.valueOf(10),
|
BigInteger.valueOf(10),
|
||||||
BigInteger.valueOf(100),
|
BigInteger.valueOf(100),
|
||||||
|
@ -4175,7 +4152,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
private static final int BIG_TEN_POWERS_TABLE_MAX =
|
private static final int BIG_TEN_POWERS_TABLE_MAX =
|
||||||
16 * BIG_TEN_POWERS_TABLE_INITLEN;
|
16 * BIG_TEN_POWERS_TABLE_INITLEN;
|
||||||
|
|
||||||
private static final long THRESHOLDS_TABLE[] = {
|
private static final long[] THRESHOLDS_TABLE = {
|
||||||
Long.MAX_VALUE, // 0
|
Long.MAX_VALUE, // 0
|
||||||
Long.MAX_VALUE/10L, // 1
|
Long.MAX_VALUE/10L, // 1
|
||||||
Long.MAX_VALUE/100L, // 2
|
Long.MAX_VALUE/100L, // 2
|
||||||
|
@ -4908,7 +4885,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||||
* @throws ArithmeticException if scale overflows.
|
* @throws ArithmeticException if scale overflows.
|
||||||
*/
|
*/
|
||||||
private static BigDecimal createAndStripZerosToMatchScale(BigInteger intVal, int scale, long preferredScale) {
|
private static BigDecimal createAndStripZerosToMatchScale(BigInteger intVal, int scale, long preferredScale) {
|
||||||
BigInteger qr[]; // quotient-remainder pair
|
BigInteger[] qr; // quotient-remainder pair
|
||||||
while (intVal.compareMagnitude(BigInteger.TEN) >= 0
|
while (intVal.compareMagnitude(BigInteger.TEN) >= 0
|
||||||
&& scale > preferredScale) {
|
&& scale > preferredScale) {
|
||||||
if (intVal.testBit(0))
|
if (intVal.testBit(0))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
* @library /test/lib
|
* @library /test/lib
|
||||||
* @build jdk.test.lib.RandomFactory
|
* @build jdk.test.lib.RandomFactory
|
||||||
* @run main StringConstructor
|
* @run main StringConstructor
|
||||||
* @bug 4103117 4331084 4488017 4490929 6255285 6268365 8074460 8078672
|
* @bug 4103117 4331084 4488017 4490929 6255285 6268365 8074460 8078672 8233760
|
||||||
* @summary Tests the BigDecimal string constructor (use -Dseed=X to set PRNG seed).
|
* @summary Tests the BigDecimal string constructor (use -Dseed=X to set PRNG seed).
|
||||||
* @key randomness
|
* @key randomness
|
||||||
*/
|
*/
|
||||||
|
@ -66,11 +66,20 @@ public class StringConstructor {
|
||||||
constructWithError("10e"+Integer.MIN_VALUE);
|
constructWithError("10e"+Integer.MIN_VALUE);
|
||||||
constructWithError("0.01e"+Integer.MIN_VALUE);
|
constructWithError("0.01e"+Integer.MIN_VALUE);
|
||||||
constructWithError("1e"+((long)Integer.MIN_VALUE-1));
|
constructWithError("1e"+((long)Integer.MIN_VALUE-1));
|
||||||
constructWithError("1e"+((long)Integer.MAX_VALUE + 1));
|
|
||||||
|
|
||||||
leadingExponentZeroTest();
|
leadingExponentZeroTest();
|
||||||
nonAsciiZeroTest();
|
nonAsciiZeroTest();
|
||||||
|
|
||||||
|
/* These BigDecimals produce a string with an exponent > Integer.MAX_VALUE */
|
||||||
|
roundtripWithAbnormalExponent(BigDecimal.valueOf(10, Integer.MIN_VALUE));
|
||||||
|
roundtripWithAbnormalExponent(BigDecimal.valueOf(Long.MIN_VALUE, Integer.MIN_VALUE));
|
||||||
|
roundtripWithAbnormalExponent(new BigDecimal(new BigInteger("1" + "0".repeat(100)), Integer.MIN_VALUE));
|
||||||
|
|
||||||
|
/* These Strings have an exponent > Integer.MAX_VALUE */
|
||||||
|
roundtripWithAbnormalExponent("1.0E+2147483649");
|
||||||
|
roundtripWithAbnormalExponent("-9.223372036854775808E+2147483666");
|
||||||
|
roundtripWithAbnormalExponent("1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+2147483748");
|
||||||
|
|
||||||
// Roundtrip tests
|
// Roundtrip tests
|
||||||
Random random = RandomFactory.getRandom();
|
Random random = RandomFactory.getRandom();
|
||||||
for (int i=0; i<100; i++) {
|
for (int i=0; i<100; i++) {
|
||||||
|
@ -95,6 +104,17 @@ public class StringConstructor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void roundtripWithAbnormalExponent(BigDecimal bd) {
|
||||||
|
if (!bd.equals(new BigDecimal(bd.toString()))) {
|
||||||
|
throw new RuntimeException("Abnormal exponent roundtrip failure");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void roundtripWithAbnormalExponent(String s) {
|
||||||
|
if (!s.equals(new BigDecimal(s).toString())) {
|
||||||
|
throw new RuntimeException("Abnormal exponent roundtrip failure");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Verify precision is set properly if the significand has
|
* Verify precision is set properly if the significand has
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue