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

java.math.BigDecimal.divide(BigDecimal, RoundingMode) produces incorrect result

    XMLWordPrintable

Details

    • b51
    • x86
    • os_x
    • Verified

    Backports

      Description

        FULL PRODUCT VERSION :
        OS X:
        Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
        Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

        Windows:
        java version "1.8.0_25"
        Java(TM) SE Runtime Environment (build 1.8.0_25-b18)
        Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)


        ADDITIONAL OS VERSION INFORMATION :
        OS X Version 10.9.5
        Microsoft Windows 7 Home Premium 6.1.6701.17514


        A DESCRIPTION OF THE PROBLEM :
        BigDecimal division with rounding mode argument and optionally with scale delivers wrong result for the following arguments:

        dividend: Long.MAX_VALUE or -LONG.MAX_VALUE, any scale
        divisor: value one with scale 17 (i.e. 1.00000000000000000)
        (examples see code BigDecimalDivideByOne)

        By the javadoc specification the preferred scale of divide(BigDecimal, RoundingMode) is this.scale, hence the input value should be identical to the output value. The method delegates to divide(BigDecimal divisor, int scale, int roundingMode) hence this method also delivers wrong results for the same arguments (with any scale).

        The problem can also be triggered for a divisor different from one if all of the following criteria hold:
        * dividend scale is negative: scale1 < 0
        * divisor is 10 to the power of -scale1: divisor=10^-scale1
        * divisor scale is dividend scale plus 17: scale2 = scale1 + 17
        * result scale is 0
        (examples see code BigDecimalDivideByPow10)

        The problem does not occur if run with JRE6 or JRE7 hence it must have been introduced with JDK8.

        REGRESSION. Last worked in version 7u72

        ADDITIONAL REGRESSION INFORMATION:
        OS X:
        Java(TM) SE Runtime Environment (build 1.7.0_72-b14)
        Java HotSpot(TM) 64-Bit Server VM (build 24.72-b04, mixed mode)

        Windows:
        java version "1.7.0_72"
        Java(TM) SE Runtime Environment (build 1.7.0_72-b14)
        Java HotSpot(TM) 64-Bit Server VM (build 24.72-b04, mixed mode)

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        Execute this code:

            public static void main(String[] args) {
             //problematic divisor: one with scale 17
             BigDecimal one = BigDecimal.ONE.setScale(17);

             int scale;
             RoundingMode rounding = RoundingMode.DOWN;
            
             //scale 17 : dividend and divisor have same scale
             scale = 17;
             System.out.println(BigDecimal.valueOf(Long.MAX_VALUE, scale).divide(one, rounding));
             System.out.println(BigDecimal.valueOf(-Long.MAX_VALUE, scale).divide(one, rounding));
            
             //scale 0
             System.out.println(BigDecimal.valueOf(Long.MAX_VALUE).divide(one, rounding));
             System.out.println(BigDecimal.valueOf(-Long.MAX_VALUE).divide(one, rounding));
            
             //any other scale, any other rounding mode
             scale = 100;//any scale reproduces the error
             System.out.println(BigDecimal.valueOf(Long.MAX_VALUE, scale).divide(one, rounding));
             rounding = RoundingMode.UNNECESSARY;
             System.out.println(BigDecimal.valueOf(-Long.MAX_VALUE, scale).divide(one, rounding));
            }


        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        92.23372036854775807
        -92.23372036854775807
        9223372036854775807
        -9223372036854775807
        9.223372036854775807E-82
        -9.223372036854775807E-82

        ACTUAL -
        92.23372032559808512
        -92.23372032559808512
        9223372032559808512
        -9223372032559808512
        9.223372032559808512E-82
        Exception in thread "main" java.lang.ArithmeticException: Rounding necessary

        ERROR MESSAGES/STACK TRACES THAT OCCUR :
        Exception in thread "main" java.lang.ArithmeticException: Rounding necessary
        at java.math.BigDecimal.commonNeedIncrement(BigDecimal.java:4148)
        at java.math.BigDecimal.needIncrement(BigDecimal.java:4204)
        at java.math.BigDecimal.divideAndRound128(BigDecimal.java:4867)
        at java.math.BigDecimal.multiplyDivideAndRound(BigDecimal.java:4789)
        at java.math.BigDecimal.divide(BigDecimal.java:5155)
        at java.math.BigDecimal.divide(BigDecimal.java:1561)
        at java.math.BigDecimal.divide(BigDecimal.java:1641)
        at BigDecimalDivide.main(BigDecimalDivide.java:26)


        REPRODUCIBILITY :
        This bug can be reproduced always.

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

        public class BigDecimalDivideByOne {

            public static void main(String[] args) {
             //problematic divisor: one with scale 17
             BigDecimal one = BigDecimal.ONE.setScale(17);

             int scale;
             RoundingMode rounding = RoundingMode.DOWN;
            
             //scale 17 : dividend and divisor have same scale
             scale = 17;
             System.out.println(BigDecimal.valueOf(Long.MAX_VALUE, scale).divide(one, rounding));
             System.out.println(BigDecimal.valueOf(-Long.MAX_VALUE, scale).divide(one, rounding));
            
             //scale 0
             System.out.println(BigDecimal.valueOf(Long.MAX_VALUE).divide(one, rounding));
             System.out.println(BigDecimal.valueOf(-Long.MAX_VALUE).divide(one, rounding));
            
             //any other scale, any other rounding mode
             scale = 100;//any scale reproduces the error
             System.out.println(BigDecimal.valueOf(Long.MAX_VALUE, scale).divide(one, rounding));
             rounding = RoundingMode.UNNECESSARY;
             System.out.println(BigDecimal.valueOf(-Long.MAX_VALUE, scale).divide(one, rounding));
            }
        }

        --

        import java.math.BigDecimal;
        import java.math.RoundingMode;

        public class BigDecimalDivideByPow10 {

            public static void main(String[] args) {
             final RoundingMode rounding = RoundingMode.DOWN;
             int scale1, scale2;
             BigDecimal pow10;
            
             scale1 = -100;
             scale2 = -83;
             pow10 = BigDecimal.ONE.movePointRight(-scale1).setScale(scale2);
             System.out.println(BigDecimal.valueOf(Long.MAX_VALUE, scale1).divide(pow10, 0, rounding));
             System.out.println(BigDecimal.valueOf(-Long.MAX_VALUE, scale1).divide(pow10, 0, rounding));
            
             scale1 = -17;
             scale2 = 0;
             pow10 = BigDecimal.ONE.movePointRight(-scale1).setScale(scale2);
             System.out.println(BigDecimal.valueOf(Long.MAX_VALUE, scale1).divide(pow10, 0, rounding));
             System.out.println(BigDecimal.valueOf(-Long.MAX_VALUE, scale1).divide(pow10, 0, rounding));

             scale1 = -1;
             scale2 = 16;
             pow10 = BigDecimal.ONE.movePointRight(-scale1).setScale(scale2);
             System.out.println(BigDecimal.valueOf(Long.MAX_VALUE, scale1).divide(pow10, 0, rounding));
             System.out.println(BigDecimal.valueOf(-Long.MAX_VALUE, scale1).divide(pow10, 0, rounding));
            }
        }

        ---------- END SOURCE ----------

        CUSTOMER SUBMITTED WORKAROUND :
        use scale 18 and re-scale result back to scale 17

        Attachments

          Issue Links

            Activity

              People

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

                Dates

                  Created:
                  Updated:
                  Resolved: