Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8132362

BigDecimal(String) constructor can't parse all toString() outputs

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Not an Issue
    • Icon: P4 P4
    • None
    • 8u45, 8u51, 9
    • core-libs

      FULL PRODUCT VERSION :
      java version "1.8.0_45"
      Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
      Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Darwin 14.4.0 Darwin Kernel Version 14.4.0: Thu May 28 11:35:04 PDT 2015; root:xnu-2782.30.5~1/RELEASE_X86_64 x86_64

      A DESCRIPTION OF THE PROBLEM :
      It is possible to construct a BigDecimal value where the scale is at -2147483647 (i.e. minimum value), and the unscaledValue is 10:

      BigDecimal d = new BigDecimal("1E+2147483647").multiply(new BigDecimal(10));

      This will produce a sensible toString() result:

      1.0E+2147483648

      However this string will NOT parse using:

      BigDecimal("1.0E+2147483648");

      Instead this gives a NumberFormatException.

      This seems to be because the parser checks that the exponent will fit in an int - however this is not necessary, since the exponent can be decremented and the unscaledValue multiplied by 10 until the exponent fits in an int, to produce the original BigDecimal.

      Note that this does not match the documentation in the Java 8 API docs for BigDecimal, where towards the end of the toString() docs there is a note:

      "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."

      This is not true for the value shown above.

      I assume a similar thing can happen for very small BigDecimals with a positive scale outside int range.

      If this behaviour is needed for backwards compatibility (I notice there is a comment to this effect at line 489 of BigDecimal.java in 1.8.0_45 sources) then can a new constructor or factory method be added with the correct behaviour? Otherwise I guess I could try to find a correct BigDecimal parser elsewhere.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run provided test case.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      No exceptions from code, following output:

      1E+2147483647, unscaled value 1, scale -2147483647
      1.0E+2147483648, unscaled value 10, scale -2147483647

      ACTUAL -
      Following output, with exception when trying to parse BigDecimal aboveMaxScale:

      1E+2147483647, unscaled value 1, scale -2147483647
      1.0E+2147483648, unscaled value 10, scale -2147483647
      Exception in thread "main" java.lang.NumberFormatException
      at java.math.BigDecimal.<init>(BigDecimal.java:491)
      at java.math.BigDecimal.<init>(BigDecimal.java:383)
      at java.math.BigDecimal.<init>(BigDecimal.java:806)
      at BigDecimalTest.main(BigDecimalTest.java:17)

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.math.BigDecimal;

      public class BigDecimalTest {
      public static void main(String[] args) {

      //This works as expected - we can parse maxScale with new BigDecimal(String)
      BigDecimal maxScale = new BigDecimal("1E+2147483647");
      System.out.println(maxScale.toString() + ", unscaled value " + maxScale.unscaledValue() + ", scale " + maxScale.scale());
      BigDecimal parseMaxScale = new BigDecimal(maxScale.toString());

      //This fails - aboveMaxScale is represented as "10" with maximum scale, so
      //produces an exponent above maximum scale when printed, and this can't be parsed.
      BigDecimal aboveMaxScale = maxScale.multiply(new BigDecimal(10));
      System.out.println(aboveMaxScale.toString() + ", unscaled value " + aboveMaxScale.unscaledValue() + ", scale " + aboveMaxScale.scale());
      BigDecimal parseAboveMaxScale = new BigDecimal(aboveMaxScale.toString());
      }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Could use my own code to parse BigDecimal output.

            bpb Brian Burkhalter
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: