JDK-8233760 : Result of BigDecimal.toString throws overflow exception on new BigDecimal(str)
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.math
  • Affected Version: 11,14,18
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2019-11-04
  • Updated: 2022-08-09
  • Resolved: 2022-06-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 19
19 b26Fixed
Related Reports
CSR :  
Description
A DESCRIPTION OF THE PROBLEM :
Based upon the javadocs for BigDecimal, the lower bound for scale is -Integer.MAX_VALUE

Quote:
"The exponent consists of the character 'e' ('\u0065') or 'E' ('\u0045') followed by one or more decimal digits. The value of the exponent must lie between -Integer.MAX_VALUE (Integer.MIN_VALUE+1) and Integer.MAX_VALUE, inclusive."

Ref: https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/math/BigDecimal.html#%3Cinit%3E(java.lang.String)

If you do a .toString() on this value, I would expect to be able to deserialize the result using `new java.math.BigDecimal(str)`

java.math.BigDecimal.valueOf(Long.MIN_VALUE, -Integer.MAX_VALUE).toString => "-9.223372036854775808E+2147483665"

new java.math.BigDecimal("-9.223372036854775808E+2147483665") => Overflow Exception (because 2147483665 > Integer.MAX_VALUE of 2147483647) - and there is an (incorrect?) guard in place throwing the exception.

Via experimentation, the actual lower bound is `-Integer.MAX_VALUE+18` or `Integer.MIN_VALUE+19`:

java.math.BigDecimal.valueOf(Long.MIN_VALUE, -Integer.MAX_VALUE+18).toString = "-9.223372036854775808E+2147483647"

new java.math.BigDecimal("-9.223372036854775808E+2147483647").toString() => "-9.223372036854775808E+2147483647"


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
import java.math.BigDecimal;

String str = java.math.BigDecimal.valueOf(Long.MIN_VALUE, -Integer.MAX_VALUE);
BigDecimal bd = new BigDecimal(str);


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
It should not throw an exception.

FREQUENCY : always



Comments
Changeset: c15e10fb Author: Raffaello Giulietti <raffaello.giulietti@oracle.com> Committer: Joe Darcy <darcy@openjdk.org> Date: 2022-06-08 16:23:04 +0000 URL: https://git.openjdk.java.net/jdk/commit/c15e10fb6c35a99e80009f0a7b6a252fcbb549b7
08-06-2022

A pull request was submitted for review. URL: https://git.openjdk.java.net/jdk/pull/8905 Date: 2022-05-26 18:02:14 +0000
26-05-2022

The spec of the BigDecimal(String) constructor should drop the requirement about the exponent being in the int range. The implementation must accept any exponent, as long as the constructed BigDecimal has its scale in the int range.
26-05-2022

The specification of toString() indicates "Next, an adjusted exponent is calculated; this is the negated scale, plus the number of characters in the converted unscaled value, less one. That is, -scale+(ulength-1), where ulength is the length of the absolute value of the unscaled value in decimal digits (its precision). " which explains the difference in exponent. The constructor BigDecimal(String) however disallows exponents outside of the range [-Integer.MAX_VALUE,Integer.MAX_VALUE] so feeding the output of toString() back into this constructor is in this case problematic. As note 1 of the specification of toString() indicates "There is a one-to-one mapping between the distinguishable BigDecimal values and the result of this conversion. That is, every distinguishable BigDecimal value (unscaled value and scale) has a unique string representation as a result of using toString. If that string representation is converted back to a BigDecimal using the BigDecimal(String) constructor, then the original value will be recovered. " there does seem to be a problem here. One might think that either the foregoing note is incorrect, or the BigDecimal(String) constructor should accommodate the adjusted exponent created by toString().
07-11-2019