changeset 4546:ffada2ce20e5

7082971: More performance tuning of BigDecimal and other java.math classes Reviewed-by: darcy Contributed-by: sergey.kuksenko@oracle.com
author darcy
date Thu, 01 Sep 2011 23:00:09 -0700
parents fcb33500b325
children 812c6d4d6a58
files src/share/classes/java/math/BigDecimal.java src/share/classes/java/math/BigInteger.java src/share/classes/java/math/MutableBigInteger.java test/java/math/BigDecimal/DivideMcTests.java test/java/math/BigDecimal/FloatDoubleValueTests.java test/java/math/BigDecimal/RangeTests.java test/java/math/BigDecimal/StrippingZerosTest.java test/java/math/BigDecimal/ToPlainStringTests.java
diffstat 8 files changed, 2669 insertions(+), 821 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/math/BigDecimal.java	Thu Sep 01 13:53:59 2011 +0100
+++ b/src/share/classes/java/math/BigDecimal.java	Thu Sep 01 23:00:09 2011 -0700
@@ -215,6 +215,7 @@
  * @author  Josh Bloch
  * @author  Mike Cowlishaw
  * @author  Joseph D. Darcy
+ * @author  Sergey V. Kuksenko
  */
 public class BigDecimal extends Number implements Comparable<BigDecimal> {
     /**
@@ -224,7 +225,7 @@
      * @serial
      * @see #unscaledValue
      */
-    private volatile BigInteger intVal;
+    private final BigInteger intVal;
 
     /**
      * The scale of this BigDecimal, as returned by {@link #scale}.
@@ -232,8 +233,9 @@
      * @serial
      * @see #scale
      */
-    private int scale;  // Note: this may have any value, so
-                        // calculations must be done in longs
+    private final int scale;  // Note: this may have any value, so
+                              // calculations must be done in longs
+
     /**
      * The number of decimal digits in this BigDecimal, or 0 if the
      * number of digits are not known (lookaside information).  If
@@ -256,19 +258,19 @@
      */
     static final long INFLATED = Long.MIN_VALUE;
 
+    private static final BigInteger INFLATED_BIGINT = BigInteger.valueOf(INFLATED);
+
     /**
      * If the absolute value of the significand of this BigDecimal is
      * less than or equal to {@code Long.MAX_VALUE}, the value can be
      * compactly stored in this field and used in computations.
      */
-    private transient long intCompact;
+    private final transient long intCompact;
 
     // All 18-digit base ten strings fit into a long; not all 19-digit
     // strings will
     private static final int MAX_COMPACT_DIGITS = 18;
 
-    private static final int MAX_BIGINT_BITS = 62;
-
     /* Appease the serialization gods */
     private static final long serialVersionUID = 6108874887143696463L;
 
@@ -282,17 +284,17 @@
 
     // Cache of common small BigDecimal values.
     private static final BigDecimal zeroThroughTen[] = {
-        new BigDecimal(BigInteger.ZERO,         0,  0, 1),
-        new BigDecimal(BigInteger.ONE,          1,  0, 1),
-        new BigDecimal(BigInteger.valueOf(2),   2,  0, 1),
-        new BigDecimal(BigInteger.valueOf(3),   3,  0, 1),
-        new BigDecimal(BigInteger.valueOf(4),   4,  0, 1),
-        new BigDecimal(BigInteger.valueOf(5),   5,  0, 1),
-        new BigDecimal(BigInteger.valueOf(6),   6,  0, 1),
-        new BigDecimal(BigInteger.valueOf(7),   7,  0, 1),
-        new BigDecimal(BigInteger.valueOf(8),   8,  0, 1),
-        new BigDecimal(BigInteger.valueOf(9),   9,  0, 1),
-        new BigDecimal(BigInteger.TEN,          10, 0, 2),
+        new BigDecimal(BigInteger.ZERO,       0,  0, 1),
+        new BigDecimal(BigInteger.ONE,        1,  0, 1),
+        new BigDecimal(BigInteger.valueOf(2), 2,  0, 1),
+        new BigDecimal(BigInteger.valueOf(3), 3,  0, 1),
+        new BigDecimal(BigInteger.valueOf(4), 4,  0, 1),
+        new BigDecimal(BigInteger.valueOf(5), 5,  0, 1),
+        new BigDecimal(BigInteger.valueOf(6), 6,  0, 1),
+        new BigDecimal(BigInteger.valueOf(7), 7,  0, 1),
+        new BigDecimal(BigInteger.valueOf(8), 8,  0, 1),
+        new BigDecimal(BigInteger.valueOf(9), 9,  0, 1),
+        new BigDecimal(BigInteger.TEN,        10, 0, 2),
     };
 
     // Cache of zero scaled by 0 - 15
@@ -378,178 +380,7 @@
      * @since  1.5
      */
     public BigDecimal(char[] in, int offset, int len) {
-        // protect against huge length.
-        if (offset+len > in.length || offset < 0)
-            throw new NumberFormatException();
-        // This is the primary string to BigDecimal constructor; all
-        // incoming strings end up here; it uses explicit (inline)
-        // parsing for speed and generates at most one intermediate
-        // (temporary) object (a char[] array) for non-compact case.
-
-        // Use locals for all fields values until completion
-        int prec = 0;                 // record precision value
-        int scl = 0;                  // record scale value
-        long rs = 0;                  // the compact value in long
-        BigInteger rb = null;         // the inflated value in BigInteger
-
-        // use array bounds checking to handle too-long, len == 0,
-        // bad offset, etc.
-        try {
-            // handle the sign
-            boolean isneg = false;          // assume positive
-            if (in[offset] == '-') {
-                isneg = true;               // leading minus means negative
-                offset++;
-                len--;
-            } else if (in[offset] == '+') { // leading + allowed
-                offset++;
-                len--;
-            }
-
-            // should now be at numeric part of the significand
-            boolean dot = false;             // true when there is a '.'
-            int cfirst = offset;             // record start of integer
-            long exp = 0;                    // exponent
-            char c;                          // current character
-
-            boolean isCompact = (len <= MAX_COMPACT_DIGITS);
-            // integer significand array & idx is the index to it. The array
-            // is ONLY used when we can't use a compact representation.
-            char coeff[] = isCompact ? null : new char[len];
-            int idx = 0;
-
-            for (; len > 0; offset++, len--) {
-                c = in[offset];
-                // have digit
-                if ((c >= '0' && c <= '9') || Character.isDigit(c)) {
-                    // First compact case, we need not to preserve the character
-                    // and we can just compute the value in place.
-                    if (isCompact) {
-                        int digit = Character.digit(c, 10);
-                        if (digit == 0) {
-                            if (prec == 0)
-                                prec = 1;
-                            else if (rs != 0) {
-                                rs *= 10;
-                                ++prec;
-                            } // else digit is a redundant leading zero
-                        } else {
-                            if (prec != 1 || rs != 0)
-                                ++prec; // prec unchanged if preceded by 0s
-                            rs = rs * 10 + digit;
-                        }
-                    } else { // the unscaled value is likely a BigInteger object.
-                        if (c == '0' || Character.digit(c, 10) == 0) {
-                            if (prec == 0) {
-                                coeff[idx] = c;
-                                prec = 1;
-                            } else if (idx != 0) {
-                                coeff[idx++] = c;
-                                ++prec;
-                            } // else c must be a redundant leading zero
-                        } else {
-                            if (prec != 1 || idx != 0)
-                                ++prec; // prec unchanged if preceded by 0s
-                            coeff[idx++] = c;
-                        }
-                    }
-                    if (dot)
-                        ++scl;
-                    continue;
-                }
-                // have dot
-                if (c == '.') {
-                    // have dot
-                    if (dot)         // two dots
-                        throw new NumberFormatException();
-                    dot = true;
-                    continue;
-                }
-                // exponent expected
-                if ((c != 'e') && (c != 'E'))
-                    throw new NumberFormatException();
-                offset++;
-                c = in[offset];
-                len--;
-                boolean negexp = (c == '-');
-                // optional sign
-                if (negexp || c == '+') {
-                    offset++;
-                    c = in[offset];
-                    len--;
-                }
-                if (len <= 0)    // no exponent digits
-                    throw new NumberFormatException();
-                // skip leading zeros in the exponent
-                while (len > 10 && Character.digit(c, 10) == 0) {
-                    offset++;
-                    c = in[offset];
-                    len--;
-                }
-                if (len > 10)  // too many nonzero exponent digits
-                    throw new NumberFormatException();
-                // c now holds first digit of exponent
-                for (;; len--) {
-                    int v;
-                    if (c >= '0' && c <= '9') {
-                        v = c - '0';
-                    } else {
-                        v = Character.digit(c, 10);
-                        if (v < 0)            // not a digit
-                            throw new NumberFormatException();
-                    }
-                    exp = exp * 10 + v;
-                    if (len == 1)
-                        break;               // that was final character
-                    offset++;
-                    c = in[offset];
-                }
-                if (negexp)                  // apply sign
-                    exp = -exp;
-                // Next test is required for backwards compatibility
-                if ((int)exp != exp)         // overflow
-                    throw new NumberFormatException();
-                break;                       // [saves a test]
-            }
-            // here when no characters left
-            if (prec == 0)              // no digits found
-                throw new NumberFormatException();
-
-            // Adjust scale if exp is not zero.
-            if (exp != 0) {                  // had significant exponent
-                // Can't call checkScale which relies on proper fields value
-                long adjustedScale = scl - exp;
-                if (adjustedScale > Integer.MAX_VALUE ||
-                    adjustedScale < Integer.MIN_VALUE)
-                    throw new NumberFormatException("Scale out of range.");
-                scl = (int)adjustedScale;
-            }
-
-            // Remove leading zeros from precision (digits count)
-            if (isCompact) {
-                rs = isneg ? -rs : rs;
-            } else {
-                char quick[];
-                if (!isneg) {
-                    quick = (coeff.length != prec) ?
-                        Arrays.copyOf(coeff, prec) : coeff;
-                } else {
-                    quick = new char[prec + 1];
-                    quick[0] = '-';
-                    System.arraycopy(coeff, 0, quick, 1, prec);
-                }
-                rb = new BigInteger(quick);
-                rs = compactValFor(rb);
-            }
-        } catch (ArrayIndexOutOfBoundsException e) {
-            throw new NumberFormatException();
-        } catch (NegativeArraySizeException e) {
-            throw new NumberFormatException();
-        }
-        this.scale = scl;
-        this.precision = prec;
-        this.intCompact = rs;
-        this.intVal = (rs != INFLATED) ? null : rb;
+        this(in,offset,len,MathContext.UNLIMITED);
     }
 
     /**
@@ -576,9 +407,254 @@
      * @since  1.5
      */
     public BigDecimal(char[] in, int offset, int len, MathContext mc) {
-        this(in, offset, len);
-        if (mc.precision > 0)
-            roundThis(mc);
+        // protect against huge length.
+        if (offset + len > in.length || offset < 0)
+            throw new NumberFormatException("Bad offset or len arguments for char[] input.");
+        // This is the primary string to BigDecimal constructor; all
+        // incoming strings end up here; it uses explicit (inline)
+        // parsing for speed and generates at most one intermediate
+        // (temporary) object (a char[] array) for non-compact case.
+
+        // Use locals for all fields values until completion
+        int prec = 0;                 // record precision value
+        int scl = 0;                  // record scale value
+        long rs = 0;                  // the compact value in long
+        BigInteger rb = null;         // the inflated value in BigInteger
+        // use array bounds checking to handle too-long, len == 0,
+        // bad offset, etc.
+        try {
+            // handle the sign
+            boolean isneg = false;          // assume positive
+            if (in[offset] == '-') {
+                isneg = true;               // leading minus means negative
+                offset++;
+                len--;
+            } else if (in[offset] == '+') { // leading + allowed
+                offset++;
+                len--;
+            }
+
+            // should now be at numeric part of the significand
+            boolean dot = false;             // true when there is a '.'
+            long exp = 0;                    // exponent
+            char c;                          // current character
+            boolean isCompact = (len <= MAX_COMPACT_DIGITS);
+            // integer significand array & idx is the index to it. The array
+            // is ONLY used when we can't use a compact representation.
+            int idx = 0;
+            if (isCompact) {
+                // First compact case, we need not to preserve the character
+                // and we can just compute the value in place.
+                for (; len > 0; offset++, len--) {
+                    c = in[offset];
+                    if ((c == '0')) { // have zero
+                        if (prec == 0)
+                            prec = 1;
+                        else if (rs != 0) {
+                            rs *= 10;
+                            ++prec;
+                        } // else digit is a redundant leading zero
+                        if (dot)
+                            ++scl;
+                    } else if ((c >= '1' && c <= '9')) { // have digit
+                        int digit = c - '0';
+                        if (prec != 1 || rs != 0)
+                            ++prec; // prec unchanged if preceded by 0s
+                        rs = rs * 10 + digit;
+                        if (dot)
+                            ++scl;
+                    } else if (c == '.') {   // have dot
+                        // have dot
+                        if (dot) // two dots
+                            throw new NumberFormatException();
+                        dot = true;
+                    } else if (Character.isDigit(c)) { // slow path
+                        int digit = Character.digit(c, 10);
+                        if (digit == 0) {
+                            if (prec == 0)
+                                prec = 1;
+                            else if (rs != 0) {
+                                rs *= 10;
+                                ++prec;
+                            } // else digit is a redundant leading zero
+                        } else {
+                            if (prec != 1 || rs != 0)
+                                ++prec; // prec unchanged if preceded by 0s
+                            rs = rs * 10 + digit;
+                        }
+                        if (dot)
+                            ++scl;
+                    } else if ((c == 'e') || (c == 'E')) {
+                        exp = parseExp(in, offset, len);
+                        // Next test is required for backwards compatibility
+                        if ((int) exp != exp) // overflow
+                            throw new NumberFormatException();
+                        break; // [saves a test]
+                    } else {
+                        throw new NumberFormatException();
+                    }
+                }
+                if (prec == 0) // no digits found
+                    throw new NumberFormatException();
+                // Adjust scale if exp is not zero.
+                if (exp != 0) { // had significant exponent
+                    scl = adjustScale(scl, exp);
+                }
+                rs = isneg ? -rs : rs;
+                int mcp = mc.precision;
+                int drop = prec - mcp; // prec has range [1, MAX_INT], mcp has range [0, MAX_INT];
+                                       // therefore, this subtract cannot overflow
+                if (mcp > 0 && drop > 0) {  // do rounding
+                    while (drop > 0) {
+                        scl = checkScaleNonZero((long) scl - drop);
+                        rs = divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                        prec = longDigitLength(rs);
+                        drop = prec - mcp;
+                    }
+                }
+            } else {
+                char coeff[] = new char[len];
+                for (; len > 0; offset++, len--) {
+                    c = in[offset];
+                    // have digit
+                    if ((c >= '0' && c <= '9') || Character.isDigit(c)) {
+                        // First compact case, we need not to preserve the character
+                        // and we can just compute the value in place.
+                        if (c == '0' || Character.digit(c, 10) == 0) {
+                            if (prec == 0) {
+                                coeff[idx] = c;
+                                prec = 1;
+                            } else if (idx != 0) {
+                                coeff[idx++] = c;
+                                ++prec;
+                            } // else c must be a redundant leading zero
+                        } else {
+                            if (prec != 1 || idx != 0)
+                                ++prec; // prec unchanged if preceded by 0s
+                            coeff[idx++] = c;
+                        }
+                        if (dot)
+                            ++scl;
+                        continue;
+                    }
+                    // have dot
+                    if (c == '.') {
+                        // have dot
+                        if (dot) // two dots
+                            throw new NumberFormatException();
+                        dot = true;
+                        continue;
+                    }
+                    // exponent expected
+                    if ((c != 'e') && (c != 'E'))
+                        throw new NumberFormatException();
+                    exp = parseExp(in, offset, len);
+                    // Next test is required for backwards compatibility
+                    if ((int) exp != exp) // overflow
+                        throw new NumberFormatException();
+                    break; // [saves a test]
+                }
+                // here when no characters left
+                if (prec == 0) // no digits found
+                    throw new NumberFormatException();
+                // Adjust scale if exp is not zero.
+                if (exp != 0) { // had significant exponent
+                    scl = adjustScale(scl, exp);
+                }
+                // Remove leading zeros from precision (digits count)
+                rb = new BigInteger(coeff, isneg ? -1 : 1, prec);
+                rs = compactValFor(rb);
+                int mcp = mc.precision;
+                if (mcp > 0 && (prec > mcp)) {
+                    if (rs == INFLATED) {
+                        int drop = prec - mcp;
+                        while (drop > 0) {
+                            scl = checkScaleNonZero((long) scl - drop);
+                            rb = divideAndRoundByTenPow(rb, drop, mc.roundingMode.oldMode);
+                            rs = compactValFor(rb);
+                            if (rs != INFLATED) {
+                                prec = longDigitLength(rs);
+                                break;
+                            }
+                            prec = bigDigitLength(rb);
+                            drop = prec - mcp;
+                        }
+                    }
+                    if (rs != INFLATED) {
+                        int drop = prec - mcp;
+                        while (drop > 0) {
+                            scl = checkScaleNonZero((long) scl - drop);
+                            rs = divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                            prec = longDigitLength(rs);
+                            drop = prec - mcp;
+                        }
+                        rb = null;
+                    }
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            throw new NumberFormatException();
+        } catch (NegativeArraySizeException e) {
+            throw new NumberFormatException();
+        }
+        this.scale = scl;
+        this.precision = prec;
+        this.intCompact = rs;
+        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
+     */
+    private static long parseExp(char[] in, int offset, int len){
+        long exp = 0;
+        offset++;
+        char c = in[offset];
+        len--;
+        boolean negexp = (c == '-');
+        // optional sign
+        if (negexp || c == '+') {
+            offset++;
+            c = in[offset];
+            len--;
+        }
+        if (len <= 0) // no exponent digits
+            throw new NumberFormatException();
+        // skip leading zeros in the exponent
+        while (len > 10 && (c=='0' || (Character.digit(c, 10) == 0))) {
+            offset++;
+            c = in[offset];
+            len--;
+        }
+        if (len > 10) // too many nonzero exponent digits
+            throw new NumberFormatException();
+        // c now holds first digit of exponent
+        for (;; len--) {
+            int v;
+            if (c >= '0' && c <= '9') {
+                v = c - '0';
+            } else {
+                v = Character.digit(c, 10);
+                if (v < 0) // not a digit
+                    throw new NumberFormatException();
+            }
+            exp = exp * 10 + v;
+            if (len == 1)
+                break; // that was final character
+            offset++;
+            c = in[offset];
+        }
+        if (negexp) // apply sign
+            exp = -exp;
+        return exp;
     }
 
     /**
@@ -754,9 +830,7 @@
      * @since  1.5
      */
     public BigDecimal(String val, MathContext mc) {
-        this(val.toCharArray(), 0, val.length());
-        if (mc.precision > 0)
-            roundThis(mc);
+        this(val.toCharArray(), 0, val.length(), mc);
     }
 
     /**
@@ -804,49 +878,7 @@
      * @throws NumberFormatException if {@code val} is infinite or NaN.
      */
     public BigDecimal(double val) {
-        if (Double.isInfinite(val) || Double.isNaN(val))
-            throw new NumberFormatException("Infinite or NaN");
-
-        // Translate the double into sign, exponent and significand, according
-        // to the formulae in JLS, Section 20.10.22.
-        long valBits = Double.doubleToLongBits(val);
-        int sign = ((valBits >> 63)==0 ? 1 : -1);
-        int exponent = (int) ((valBits >> 52) & 0x7ffL);
-        long significand = (exponent==0 ? (valBits & ((1L<<52) - 1)) << 1
-                            : (valBits & ((1L<<52) - 1)) | (1L<<52));
-        exponent -= 1075;
-        // At this point, val == sign * significand * 2**exponent.
-
-        /*
-         * Special case zero to supress nonterminating normalization
-         * and bogus scale calculation.
-         */
-        if (significand == 0) {
-            intVal = BigInteger.ZERO;
-            intCompact = 0;
-            precision = 1;
-            return;
-        }
-
-        // Normalize
-        while((significand & 1) == 0) {    //  i.e., significand is even
-            significand >>= 1;
-            exponent++;
-        }
-
-        // Calculate intVal and scale
-        long s = sign * significand;
-        BigInteger b;
-        if (exponent < 0) {
-            b = BigInteger.valueOf(5).pow(-exponent).multiply(s);
-            scale = -exponent;
-        } else if (exponent > 0) {
-            b = BigInteger.valueOf(2).pow(exponent).multiply(s);
-        } else {
-            b = BigInteger.valueOf(s);
-        }
-        intCompact = compactValFor(b);
-        intVal = (intCompact != INFLATED) ? null : b;
+        this(val,MathContext.UNLIMITED);
     }
 
     /**
@@ -868,9 +900,86 @@
      * @since  1.5
      */
     public BigDecimal(double val, MathContext mc) {
-        this(val);
-        if (mc.precision > 0)
-            roundThis(mc);
+        if (Double.isInfinite(val) || Double.isNaN(val))
+            throw new NumberFormatException("Infinite or NaN");
+        // Translate the double into sign, exponent and significand, according
+        // to the formulae in JLS, Section 20.10.22.
+        int sign = (val >= 0.0 ? 1 : -1); // Preserving sign of zero doesn't matter
+        int exponent = Math.getExponent(val);
+        long valBits = Double.doubleToLongBits(val);
+        long significand = (exponent == (Double.MIN_EXPONENT-1)
+                            ? (valBits & ((1L << 52) - 1)) << 1
+                            : (valBits & ((1L << 52) - 1)) | (1L << 52));
+        // At this point, val == sign * significand * 2**exponent.
+
+        /*
+         * Special case zero to supress nonterminating normalization and bogus
+         * scale calculation.
+         */
+        if (significand == 0) {
+            this.intVal = BigInteger.ZERO;
+            this.scale = 0;
+            this.intCompact = 0;
+            this.precision = 1;
+            return;
+        }
+        // Normalize
+        while ((significand & 1) == 0) { // i.e., significand is even
+            significand >>= 1;
+            exponent++;
+        }
+        int scale = 0;
+        // Calculate intVal and scale
+        BigInteger intVal;
+        long compactVal = sign * significand;
+        if (exponent == 0) {
+            // If the exponent is zero, the significant fits in a long
+            assert compactVal != INFLATED;
+            intVal = null;
+        } else {
+            if (exponent < 0) {
+                intVal = BigInteger.valueOf(5).pow(-exponent).multiply(compactVal);
+                scale = -exponent;
+            } else { //  (exponent > 0)
+                intVal = BigInteger.valueOf(2).pow(exponent).multiply(compactVal);
+            }
+            compactVal = compactValFor(intVal);
+        }
+        int prec = 0;
+        int mcp = mc.precision;
+        if (mcp > 0) { // do rounding
+            int mode = mc.roundingMode.oldMode;
+            int drop;
+            if (compactVal == INFLATED) {
+                prec = bigDigitLength(intVal);
+                drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    intVal = divideAndRoundByTenPow(intVal, drop, mode);
+                    compactVal = compactValFor(intVal);
+                    if (compactVal != INFLATED) {
+                        break;
+                    }
+                    prec = bigDigitLength(intVal);
+                    drop = prec - mcp;
+                }
+            }
+            if (compactVal != INFLATED) {
+                prec = longDigitLength(compactVal);
+                drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                    prec = longDigitLength(compactVal);
+                    drop = prec - mcp;
+                }
+                intVal = null;
+            }
+        }
+        this.intVal = intVal;
+        this.intCompact = compactVal;
+        this.scale = scale;
+        this.precision = prec;
     }
 
     /**
@@ -881,8 +990,9 @@
      *            {@code BigDecimal}.
      */
     public BigDecimal(BigInteger val) {
+        scale = 0;
+        intVal = val;
         intCompact = compactValFor(val);
-        intVal = (intCompact != INFLATED) ? null : val;
     }
 
     /**
@@ -898,9 +1008,7 @@
      * @since  1.5
      */
     public BigDecimal(BigInteger val, MathContext mc) {
-        this(val);
-        if (mc.precision > 0)
-            roundThis(mc);
+        this(val,0,mc);
     }
 
     /**
@@ -914,7 +1022,8 @@
      */
     public BigDecimal(BigInteger unscaledVal, int scale) {
         // Negative scales are now allowed
-        this(unscaledVal);
+        this.intVal = unscaledVal;
+        this.intCompact = compactValFor(unscaledVal);
         this.scale = scale;
     }
 
@@ -934,10 +1043,41 @@
      * @since  1.5
      */
     public BigDecimal(BigInteger unscaledVal, int scale, MathContext mc) {
-        this(unscaledVal);
+        long compactVal = compactValFor(unscaledVal);
+        int mcp = mc.precision;
+        int prec = 0;
+        if (mcp > 0) { // do rounding
+            int mode = mc.roundingMode.oldMode;
+            if (compactVal == INFLATED) {
+                prec = bigDigitLength(unscaledVal);
+                int drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    unscaledVal = divideAndRoundByTenPow(unscaledVal, drop, mode);
+                    compactVal = compactValFor(unscaledVal);
+                    if (compactVal != INFLATED) {
+                        break;
+                    }
+                    prec = bigDigitLength(unscaledVal);
+                    drop = prec - mcp;
+                }
+            }
+            if (compactVal != INFLATED) {
+                prec = longDigitLength(compactVal);
+                int drop = prec - mcp;     // drop can't be more than 18
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mode);
+                    prec = longDigitLength(compactVal);
+                    drop = prec - mcp;
+                }
+                unscaledVal = null;
+            }
+        }
+        this.intVal = unscaledVal;
+        this.intCompact = compactVal;
         this.scale = scale;
-        if (mc.precision > 0)
-            roundThis(mc);
+        this.precision = prec;
     }
 
     /**
@@ -949,7 +1089,9 @@
      * @since  1.5
      */
     public BigDecimal(int val) {
-        intCompact = val;
+        this.intCompact = val;
+        this.scale = 0;
+        this.intVal = null;
     }
 
     /**
@@ -964,9 +1106,24 @@
      * @since  1.5
      */
     public BigDecimal(int val, MathContext mc) {
-        intCompact = val;
-        if (mc.precision > 0)
-            roundThis(mc);
+        int mcp = mc.precision;
+        long compactVal = val;
+        int scale = 0;
+        int prec = 0;
+        if (mcp > 0) { // do rounding
+            prec = longDigitLength(compactVal);
+            int drop = prec - mcp; // drop can't be more than 18
+            while (drop > 0) {
+                scale = checkScaleNonZero((long) scale - drop);
+                compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                prec = longDigitLength(compactVal);
+                drop = prec - mcp;
+            }
+        }
+        this.intVal = null;
+        this.intCompact = compactVal;
+        this.scale = scale;
+        this.precision = prec;
     }
 
     /**
@@ -978,7 +1135,8 @@
      */
     public BigDecimal(long val) {
         this.intCompact = val;
-        this.intVal = (val == INFLATED) ? BigInteger.valueOf(val) : null;
+        this.intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
+        this.scale = 0;
     }
 
     /**
@@ -993,9 +1151,42 @@
      * @since  1.5
      */
     public BigDecimal(long val, MathContext mc) {
-        this(val);
-        if (mc.precision > 0)
-            roundThis(mc);
+        int mcp = mc.precision;
+        int mode = mc.roundingMode.oldMode;
+        int prec = 0;
+        int scale = 0;
+        BigInteger intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
+        if (mcp > 0) { // do rounding
+            if (val == INFLATED) {
+                prec = 19;
+                int drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    intVal = divideAndRoundByTenPow(intVal, drop, mode);
+                    val = compactValFor(intVal);
+                    if (val != INFLATED) {
+                        break;
+                    }
+                    prec = bigDigitLength(intVal);
+                    drop = prec - mcp;
+                }
+            }
+            if (val != INFLATED) {
+                prec = longDigitLength(val);
+                int drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    val = divideAndRound(val, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                    prec = longDigitLength(val);
+                    drop = prec - mcp;
+                }
+                intVal = null;
+            }
+        }
+        this.intVal = intVal;
+        this.intCompact = val;
+        this.scale = scale;
+        this.precision = prec;
     }
 
     // Static Factory Methods
@@ -1016,13 +1207,10 @@
         if (scale == 0)
             return valueOf(unscaledVal);
         else if (unscaledVal == 0) {
-            if (scale > 0 && scale < ZERO_SCALED_BY.length)
-                return ZERO_SCALED_BY[scale];
-            else
-                return new BigDecimal(BigInteger.ZERO, 0, scale, 1);
+            return zeroValueOf(scale);
         }
         return new BigDecimal(unscaledVal == INFLATED ?
-                              BigInteger.valueOf(unscaledVal) : null,
+                              INFLATED_BIGINT : null,
                               unscaledVal, scale, 0);
     }
 
@@ -1041,7 +1229,34 @@
             return zeroThroughTen[(int)val];
         else if (val != INFLATED)
             return new BigDecimal(null, val, 0, 0);
-        return new BigDecimal(BigInteger.valueOf(val), val, 0, 0);
+        return new BigDecimal(INFLATED_BIGINT, val, 0, 0);
+    }
+
+    static BigDecimal valueOf(long unscaledVal, int scale, int prec) {
+        if (scale == 0 && unscaledVal >= 0 && unscaledVal < zeroThroughTen.length) {
+            return zeroThroughTen[(int) unscaledVal];
+        } else if (unscaledVal == 0) {
+            return zeroValueOf(scale);
+        }
+        return new BigDecimal(unscaledVal == INFLATED ? INFLATED_BIGINT : null,
+                unscaledVal, scale, prec);
+    }
+
+    static BigDecimal valueOf(BigInteger intVal, int scale, int prec) {
+        long val = compactValFor(intVal);
+        if (val == 0) {
+            return zeroValueOf(scale);
+        } else if (scale == 0 && val >= 0 && val < zeroThroughTen.length) {
+            return zeroThroughTen[(int) val];
+        }
+        return new BigDecimal(intVal, val, scale, prec);
+    }
+
+    static BigDecimal zeroValueOf(int scale) {
+        if (scale >= 0 && scale < ZERO_SCALED_BY.length)
+            return ZERO_SCALED_BY[scale];
+        else
+            return new BigDecimal(BigInteger.ZERO, 0, scale, 1);
     }
 
     /**
@@ -1079,42 +1294,19 @@
      * @return {@code this + augend}
      */
     public BigDecimal add(BigDecimal augend) {
-        long xs = this.intCompact;
-        long ys = augend.intCompact;
-        BigInteger fst = (xs != INFLATED) ? null : this.intVal;
-        BigInteger snd = (ys != INFLATED) ? null : augend.intVal;
-        int rscale = this.scale;
-
-        long sdiff = (long)rscale - augend.scale;
-        if (sdiff != 0) {
-            if (sdiff < 0) {
-                int raise = checkScale(-sdiff);
-                rscale = augend.scale;
-                if (xs == INFLATED ||
-                    (xs = longMultiplyPowerTen(xs, raise)) == INFLATED)
-                    fst = bigMultiplyPowerTen(raise);
+        if (this.intCompact != INFLATED) {
+            if ((augend.intCompact != INFLATED)) {
+                return add(this.intCompact, this.scale, augend.intCompact, augend.scale);
             } else {
-                int raise = augend.checkScale(sdiff);
-                if (ys == INFLATED ||
-                    (ys = longMultiplyPowerTen(ys, raise)) == INFLATED)
-                    snd = augend.bigMultiplyPowerTen(raise);
+                return add(this.intCompact, this.scale, augend.intVal, augend.scale);
+            }
+        } else {
+            if ((augend.intCompact != INFLATED)) {
+                return add(augend.intCompact, augend.scale, this.intVal, this.scale);
+            } else {
+                return add(this.intVal, this.scale, augend.intVal, augend.scale);
             }
         }
-        if (xs != INFLATED && ys != INFLATED) {
-            long sum = xs + ys;
-            // See "Hacker's Delight" section 2-12 for explanation of
-            // the overflow test.
-            if ( (((sum ^ xs) & (sum ^ ys))) >= 0L) // not overflowed
-                return BigDecimal.valueOf(sum, rscale);
-        }
-        if (fst == null)
-            fst = BigInteger.valueOf(xs);
-        if (snd == null)
-            snd = BigInteger.valueOf(ys);
-        BigInteger sum = fst.add(snd);
-        return (fst.signum == snd.signum) ?
-            new BigDecimal(sum, INFLATED, rscale, 0) :
-            new BigDecimal(sum, rscale);
     }
 
     /**
@@ -1136,10 +1328,6 @@
             return add(augend);
         BigDecimal lhs = this;
 
-        // Could optimize if values are compact
-        this.inflate();
-        augend.inflate();
-
         // If either number is zero then the other number, rounded and
         // scaled if necessary, is used as the result.
         {
@@ -1150,20 +1338,14 @@
                 int preferredScale = Math.max(lhs.scale(), augend.scale());
                 BigDecimal result;
 
-                // Could use a factory for zero instead of a new object
                 if (lhsIsZero && augendIsZero)
-                    return new BigDecimal(BigInteger.ZERO, 0, preferredScale, 0);
-
+                    return zeroValueOf(preferredScale);
                 result = lhsIsZero ? doRound(augend, mc) : doRound(lhs, mc);
 
                 if (result.scale() == preferredScale)
                     return result;
                 else if (result.scale() > preferredScale) {
-                    BigDecimal scaledResult =
-                        new BigDecimal(result.intVal, result.intCompact,
-                                       result.scale, 0);
-                    scaledResult.stripZerosToMatchScale(preferredScale);
-                    return scaledResult;
+                    return stripZerosToMatchScale(result.intVal, result.intCompact, result.scale, preferredScale);
                 } else { // result.scale < preferredScale
                     int precisionDiff = mc.precision - result.precision();
                     int scaleDiff     = preferredScale - result.scale();
@@ -1176,17 +1358,14 @@
             }
         }
 
-        long padding = (long)lhs.scale - augend.scale;
-        if (padding != 0) {        // scales differ; alignment needed
+        long padding = (long) lhs.scale - augend.scale;
+        if (padding != 0) { // scales differ; alignment needed
             BigDecimal arg[] = preAlign(lhs, augend, padding, mc);
             matchScale(arg);
-            lhs    = arg[0];
+            lhs = arg[0];
             augend = arg[1];
         }
-
-        BigDecimal d = new BigDecimal(lhs.inflate().add(augend.inflate()),
-                                      lhs.scale);
-        return doRound(d, mc);
+        return doRound(lhs.inflated().add(augend.inflated()), lhs.scale, mc);
     }
 
     /**
@@ -1211,27 +1390,26 @@
      * that the number of digits of the smaller operand could be
      * reduced even though the significands partially overlapped.
      */
-    private BigDecimal[] preAlign(BigDecimal lhs, BigDecimal augend,
-                                  long padding, MathContext mc) {
+    private BigDecimal[] preAlign(BigDecimal lhs, BigDecimal augend, long padding, MathContext mc) {
         assert padding != 0;
         BigDecimal big;
         BigDecimal small;
 
-        if (padding < 0) {     // lhs is big;   augend is small
-            big   = lhs;
+        if (padding < 0) { // lhs is big; augend is small
+            big = lhs;
             small = augend;
-        } else {               // lhs is small; augend is big
-            big   = augend;
+        } else { // lhs is small; augend is big
+            big = augend;
             small = lhs;
         }
 
         /*
-         * This is the estimated scale of an ulp of the result; it
-         * assumes that the result doesn't have a carry-out on a true
-         * add (e.g. 999 + 1 => 1000) or any subtractive cancellation
-         * on borrowing (e.g. 100 - 1.2 => 98.8)
+         * This is the estimated scale of an ulp of the result; it assumes that
+         * the result doesn't have a carry-out on a true add (e.g. 999 + 1 =>
+         * 1000) or any subtractive cancellation on borrowing (e.g. 100 - 1.2 =>
+         * 98.8)
          */
-        long estResultUlpScale = (long)big.scale - big.precision() + mc.precision;
+        long estResultUlpScale = (long) big.scale - big.precision() + mc.precision;
 
         /*
          * The low-order digit position of big is big.scale().  This
@@ -1242,11 +1420,10 @@
          * disjoint *and* the digit positions of small should not be
          * directly visible in the result.
          */
-        long smallHighDigitPos = (long)small.scale - small.precision() + 1;
-        if (smallHighDigitPos > big.scale + 2 &&         // big and small disjoint
+        long smallHighDigitPos = (long) small.scale - small.precision() + 1;
+        if (smallHighDigitPos > big.scale + 2 && // big and small disjoint
             smallHighDigitPos > estResultUlpScale + 2) { // small digits not visible
-            small = BigDecimal.valueOf(small.signum(),
-                                       this.checkScale(Math.max(big.scale, estResultUlpScale) + 3));
+            small = BigDecimal.valueOf(small.signum(), this.checkScale(Math.max(big.scale, estResultUlpScale) + 3));
         }
 
         // Since addition is symmetric, preserving input order in
@@ -1264,7 +1441,22 @@
      * @return {@code this - subtrahend}
      */
     public BigDecimal subtract(BigDecimal subtrahend) {
-        return add(subtrahend.negate());
+        if (this.intCompact != INFLATED) {
+            if ((subtrahend.intCompact != INFLATED)) {
+                return add(this.intCompact, this.scale, -subtrahend.intCompact, subtrahend.scale);
+            } else {
+                return add(this.intCompact, this.scale, subtrahend.intVal.negate(), subtrahend.scale);
+            }
+        } else {
+            if ((subtrahend.intCompact != INFLATED)) {
+                // Pair of subtrahend values given before pair of
+                // values from this BigDecimal to avoid need for
+                // method overloading on the specialized add method
+                return add(-subtrahend.intCompact, subtrahend.scale, this.intVal, this.scale);
+            } else {
+                return add(this.intVal, this.scale, subtrahend.intVal.negate(), subtrahend.scale);
+            }
+        }
     }
 
     /**
@@ -1282,11 +1474,10 @@
      * @since  1.5
      */
     public BigDecimal subtract(BigDecimal subtrahend, MathContext mc) {
-        BigDecimal nsubtrahend = subtrahend.negate();
         if (mc.precision == 0)
-            return add(nsubtrahend);
+            return subtract(subtrahend);
         // share the special rounding code in add()
-        return add(nsubtrahend, mc);
+        return add(subtrahend.negate(), mc);
     }
 
     /**
@@ -1298,37 +1489,20 @@
      * @return {@code this * multiplicand}
      */
     public BigDecimal multiply(BigDecimal multiplicand) {
-        long x = this.intCompact;
-        long y = multiplicand.intCompact;
-        int productScale = checkScale((long)scale + multiplicand.scale);
-
-        // Might be able to do a more clever check incorporating the
-        // inflated check into the overflow computation.
-        if (x != INFLATED && y != INFLATED) {
-            /*
-             * If the product is not an overflowed value, continue
-             * to use the compact representation.  if either of x or y
-             * is INFLATED, the product should also be regarded as
-             * an overflow. Before using the overflow test suggested in
-             * "Hacker's Delight" section 2-12, we perform quick checks
-             * using the precision information to see whether the overflow
-             * would occur since division is expensive on most CPUs.
-             */
-            long product = x * y;
-            long prec = this.precision() + multiplicand.precision();
-            if (prec < 19 || (prec < 21 && (y == 0 || product / y == x)))
-                return BigDecimal.valueOf(product, productScale);
-            return new BigDecimal(BigInteger.valueOf(x).multiply(y), INFLATED,
-                                  productScale, 0);
+        int productScale = checkScale((long) scale + multiplicand.scale);
+        if (this.intCompact != INFLATED) {
+            if ((multiplicand.intCompact != INFLATED)) {
+                return multiply(this.intCompact, multiplicand.intCompact, productScale);
+            } else {
+                return multiply(this.intCompact, multiplicand.intVal, productScale);
+            }
+        } else {
+            if ((multiplicand.intCompact != INFLATED)) {
+                return multiply(multiplicand.intCompact, this.intVal, productScale);
+            } else {
+                return multiply(this.intVal, multiplicand.intVal, productScale);
+            }
         }
-        BigInteger rb;
-        if (x == INFLATED && y == INFLATED)
-            rb = this.intVal.multiply(multiplicand.intVal);
-        else if (x != INFLATED)
-            rb = multiplicand.intVal.multiply(x);
-        else
-            rb = this.intVal.multiply(y);
-        return new BigDecimal(rb, INFLATED, productScale, 0);
     }
 
     /**
@@ -1345,7 +1519,20 @@
     public BigDecimal multiply(BigDecimal multiplicand, MathContext mc) {
         if (mc.precision == 0)
             return multiply(multiplicand);
-        return doRound(this.multiply(multiplicand), mc);
+        int productScale = checkScale((long) scale + multiplicand.scale);
+        if (this.intCompact != INFLATED) {
+            if ((multiplicand.intCompact != INFLATED)) {
+                return multiplyAndRound(this.intCompact, multiplicand.intCompact, productScale, mc);
+            } else {
+                return multiplyAndRound(this.intCompact, multiplicand.intVal, productScale, mc);
+            }
+        } else {
+            if ((multiplicand.intCompact != INFLATED)) {
+                return multiplyAndRound(multiplicand.intCompact, this.intVal, productScale, mc);
+            } else {
+                return multiplyAndRound(this.intVal, multiplicand.intVal, productScale, mc);
+            }
+        }
     }
 
     /**
@@ -1377,120 +1564,21 @@
      * @see    #ROUND_UNNECESSARY
      */
     public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) {
-        /*
-         * IMPLEMENTATION NOTE: This method *must* return a new object
-         * since divideAndRound uses divide to generate a value whose
-         * scale is then modified.
-         */
         if (roundingMode < ROUND_UP || roundingMode > ROUND_UNNECESSARY)
             throw new IllegalArgumentException("Invalid rounding mode");
-        /*
-         * Rescale dividend or divisor (whichever can be "upscaled" to
-         * produce correctly scaled quotient).
-         * Take care to detect out-of-range scales
-         */
-        BigDecimal dividend = this;
-        if (checkScale((long)scale + divisor.scale) > this.scale)
-            dividend = this.setScale(scale + divisor.scale, ROUND_UNNECESSARY);
-        else
-            divisor = divisor.setScale(checkScale((long)this.scale - scale),
-                                       ROUND_UNNECESSARY);
-        return divideAndRound(dividend.intCompact, dividend.intVal,
-                              divisor.intCompact, divisor.intVal,
-                              scale, roundingMode, scale);
-    }
-
-    /**
-     * Internally used for division operation. The dividend and divisor are
-     * passed both in {@code long} format and {@code BigInteger} format. The
-     * returned {@code BigDecimal} object is the quotient whose scale is set to
-     * the passed in scale. If the remainder is not zero, it will be rounded
-     * based on the passed in roundingMode. Also, if the remainder is zero and
-     * the last parameter, i.e. preferredScale is NOT equal to scale, the
-     * trailing zeros of the result is stripped to match the preferredScale.
-     */
-    private static BigDecimal divideAndRound(long ldividend, BigInteger bdividend,
-                                             long ldivisor,  BigInteger bdivisor,
-                                             int scale, int roundingMode,
-                                             int preferredScale) {
-        boolean isRemainderZero;       // record remainder is zero or not
-        int qsign;                     // quotient sign
-        long q = 0, r = 0;             // store quotient & remainder in long
-        MutableBigInteger mq = null;   // store quotient
-        MutableBigInteger mr = null;   // store remainder
-        MutableBigInteger mdivisor = null;
-        boolean isLongDivision = (ldividend != INFLATED && ldivisor != INFLATED);
-        if (isLongDivision) {
-            q = ldividend / ldivisor;
-            if (roundingMode == ROUND_DOWN && scale == preferredScale)
-                return new BigDecimal(null, q, scale, 0);
-            r = ldividend % ldivisor;
-            isRemainderZero = (r == 0);
-            qsign = ((ldividend < 0) == (ldivisor < 0)) ? 1 : -1;
+        if (this.intCompact != INFLATED) {
+            if ((divisor.intCompact != INFLATED)) {
+                return divide(this.intCompact, this.scale, divisor.intCompact, divisor.scale, scale, roundingMode);
+            } else {
+                return divide(this.intCompact, this.scale, divisor.intVal, divisor.scale, scale, roundingMode);
+            }
         } else {
-            if (bdividend == null)
-                bdividend = BigInteger.valueOf(ldividend);
-            // Descend into mutables for faster remainder checks
-            MutableBigInteger mdividend = new MutableBigInteger(bdividend.mag);
-            mq = new MutableBigInteger();
-            if (ldivisor != INFLATED) {
-                r = mdividend.divide(ldivisor, mq);
-                isRemainderZero = (r == 0);
-                qsign = (ldivisor < 0) ? -bdividend.signum : bdividend.signum;
+            if ((divisor.intCompact != INFLATED)) {
+                return divide(this.intVal, this.scale, divisor.intCompact, divisor.scale, scale, roundingMode);
             } else {
-                mdivisor = new MutableBigInteger(bdivisor.mag);
-                mr = mdividend.divide(mdivisor, mq);
-                isRemainderZero = mr.isZero();
-                qsign = (bdividend.signum != bdivisor.signum) ? -1 : 1;
+                return divide(this.intVal, this.scale, divisor.intVal, divisor.scale, scale, roundingMode);
             }
         }
-        boolean increment = false;
-        if (!isRemainderZero) {
-            int cmpFracHalf;
-            /* Round as appropriate */
-            if (roundingMode == ROUND_UNNECESSARY) {  // Rounding prohibited
-                throw new ArithmeticException("Rounding necessary");
-            } else if (roundingMode == ROUND_UP) {      // Away from zero
-                increment = true;
-            } else if (roundingMode == ROUND_DOWN) {    // Towards zero
-                increment = false;
-            } else if (roundingMode == ROUND_CEILING) { // Towards +infinity
-                increment = (qsign > 0);
-            } else if (roundingMode == ROUND_FLOOR) {   // Towards -infinity
-                increment = (qsign < 0);
-            } else {
-                if (isLongDivision || ldivisor != INFLATED) {
-                    if (r <= HALF_LONG_MIN_VALUE || r > HALF_LONG_MAX_VALUE) {
-                        cmpFracHalf = 1;    // 2 * r can't fit into long
-                    } else {
-                        cmpFracHalf = longCompareMagnitude(2 * r, ldivisor);
-                    }
-                } else {
-                    cmpFracHalf = mr.compareHalf(mdivisor);
-                }
-                if (cmpFracHalf < 0)
-                    increment = false;     // We're closer to higher digit
-                else if (cmpFracHalf > 0)  // We're closer to lower digit
-                    increment = true;
-                else if (roundingMode == ROUND_HALF_UP)
-                    increment = true;
-                else if (roundingMode == ROUND_HALF_DOWN)
-                    increment = false;
-                else  // roundingMode == ROUND_HALF_EVEN, true iff quotient is odd
-                    increment = isLongDivision ? (q & 1L) != 0L : mq.isOdd();
-            }
-        }
-        BigDecimal res;
-        if (isLongDivision)
-            res = new BigDecimal(null, (increment ? q + qsign : q), scale, 0);
-        else {
-            if (increment)
-                mq.add(MutableBigInteger.ONE);
-            res = mq.toBigDecimal(qsign, scale);
-        }
-        if (isRemainderZero && preferredScale != scale)
-            res.stripZerosToMatchScale(preferredScale);
-        return res;
     }
 
     /**
@@ -1541,7 +1629,7 @@
      * @see    #ROUND_UNNECESSARY
      */
     public BigDecimal divide(BigDecimal divisor, int roundingMode) {
-            return this.divide(divisor, scale, roundingMode);
+        return this.divide(divisor, scale, roundingMode);
     }
 
     /**
@@ -1588,15 +1676,11 @@
         }
 
         // Calculate preferred scale
-        int preferredScale = saturateLong((long)this.scale - divisor.scale);
-        if (this.signum() == 0)        // 0/y
-            return (preferredScale >= 0 &&
-                    preferredScale < ZERO_SCALED_BY.length) ?
-                ZERO_SCALED_BY[preferredScale] :
-                BigDecimal.valueOf(0, preferredScale);
+        int preferredScale = saturateLong((long) this.scale - divisor.scale);
+
+        if (this.signum() == 0) // 0/y
+            return zeroValueOf(preferredScale);
         else {
-            this.inflate();
-            divisor.inflate();
             /*
              * If the quotient this/divisor has a terminating decimal
              * expansion, the expansion can have no more than
@@ -1623,7 +1707,6 @@
             // the desired one by removing trailing zeros; since the
             // exact divide method does not have an explicit digit
             // limit, we can add zeros too.
-
             if (preferredScale > quotientScale)
                 return quotient.setScale(preferredScale, ROUND_UNNECESSARY);
 
@@ -1668,38 +1751,23 @@
                 throw new ArithmeticException("Division undefined");  // NaN
             throw new ArithmeticException("Division by zero");
         }
-        if (dividend.signum() == 0)        // 0/y
-            return new BigDecimal(BigInteger.ZERO, 0,
-                                  saturateLong(preferredScale), 1);
-
-        // Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+        if (dividend.signum() == 0) // 0/y
+            return zeroValueOf(saturateLong(preferredScale));
         int xscale = dividend.precision();
         int yscale = divisor.precision();
-        dividend = new BigDecimal(dividend.intVal, dividend.intCompact,
-                                  xscale, xscale);
-        divisor = new BigDecimal(divisor.intVal, divisor.intCompact,
-                                 yscale, yscale);
-        if (dividend.compareMagnitude(divisor) > 0) // satisfy constraint (b)
-            yscale = divisor.scale -= 1;            // [that is, divisor *= 10]
-
-        // In order to find out whether the divide generates the exact result,
-        // we avoid calling the above divide method. 'quotient' holds the
-        // return BigDecimal object whose scale will be set to 'scl'.
-        BigDecimal quotient;
-        int scl = checkScale(preferredScale + yscale - xscale + mcp);
-        if (checkScale((long)mcp + yscale) > xscale)
-            dividend = dividend.setScale(mcp + yscale, ROUND_UNNECESSARY);
-        else
-            divisor = divisor.setScale(checkScale((long)xscale - mcp),
-                                       ROUND_UNNECESSARY);
-        quotient = divideAndRound(dividend.intCompact, dividend.intVal,
-                                  divisor.intCompact, divisor.intVal,
-                                  scl, mc.roundingMode.oldMode,
-                                  checkScale(preferredScale));
-        // doRound, here, only affects 1000000000 case.
-        quotient = doRound(quotient, mc);
-
-        return quotient;
+        if(dividend.intCompact!=INFLATED) {
+            if(divisor.intCompact!=INFLATED) {
+                return divide(dividend.intCompact, xscale, divisor.intCompact, yscale, preferredScale, mc);
+            } else {
+                return divide(dividend.intCompact, xscale, divisor.intVal, yscale, preferredScale, mc);
+            }
+        } else {
+            if(divisor.intCompact!=INFLATED) {
+                return divide(dividend.intVal, xscale, divisor.intCompact, yscale, preferredScale, mc);
+            } else {
+                return divide(dividend.intVal, xscale, divisor.intVal, yscale, preferredScale, mc);
+            }
+        }
     }
 
     /**
@@ -1715,13 +1783,13 @@
      */
     public BigDecimal divideToIntegralValue(BigDecimal divisor) {
         // Calculate preferred scale
-        int preferredScale = saturateLong((long)this.scale - divisor.scale);
+        int preferredScale = saturateLong((long) this.scale - divisor.scale);
         if (this.compareMagnitude(divisor) < 0) {
             // much faster when this << divisor
-            return BigDecimal.valueOf(0, preferredScale);
+            return zeroValueOf(preferredScale);
         }
 
-        if(this.signum() == 0 && divisor.signum() != 0)
+        if (this.signum() == 0 && divisor.signum() != 0)
             return this.setScale(preferredScale, ROUND_UNNECESSARY);
 
         // Perform a divide with enough digits to round to a correct
@@ -1735,13 +1803,14 @@
                                                                    RoundingMode.DOWN));
         if (quotient.scale > 0) {
             quotient = quotient.setScale(0, RoundingMode.DOWN);
-            quotient.stripZerosToMatchScale(preferredScale);
+            quotient = stripZerosToMatchScale(quotient.intVal, quotient.intCompact, quotient.scale, preferredScale);
         }
 
         if (quotient.scale < preferredScale) {
             // pad with zeros if necessary
             quotient = quotient.setScale(preferredScale, ROUND_UNNECESSARY);
         }
+
         return quotient;
     }
 
@@ -1766,8 +1835,8 @@
      * @author Joseph D. Darcy
      */
     public BigDecimal divideToIntegralValue(BigDecimal divisor, MathContext mc) {
-        if (mc.precision == 0 ||                        // exact result
-            (this.compareMagnitude(divisor) < 0) )      // zero result
+        if (mc.precision == 0 || // exact result
+            (this.compareMagnitude(divisor) < 0)) // zero result
             return divideToIntegralValue(divisor);
 
         // Calculate preferred scale
@@ -1780,8 +1849,7 @@
          * digits.  Next, remove any fractional digits from the
          * quotient and adjust the scale to the preferred value.
          */
-        BigDecimal result = this.
-            divide(divisor, new MathContext(mc.precision, RoundingMode.DOWN));
+        BigDecimal result = this.divide(divisor, new MathContext(mc.precision, RoundingMode.DOWN));
 
         if (result.scale() < 0) {
             /*
@@ -1811,8 +1879,7 @@
             return result.setScale(result.scale() +
                                    Math.min(precisionDiff, preferredScale - result.scale) );
         } else {
-            result.stripZerosToMatchScale(preferredScale);
-            return result;
+            return stripZerosToMatchScale(result.intVal,result.intCompact,result.scale,preferredScale);
         }
     }
 
@@ -1954,8 +2021,7 @@
         // No need to calculate pow(n) if result will over/underflow.
         // Don't attempt to support "supernormal" numbers.
         int newScale = checkScale((long)scale * n);
-        this.inflate();
-        return new BigDecimal(intVal.pow(n), newScale);
+        return new BigDecimal(this.inflated().pow(n), newScale);
     }
 
 
@@ -2016,12 +2082,10 @@
             throw new ArithmeticException("Invalid operation");
         if (n == 0)
             return ONE;                      // x**0 == 1 in X3.274
-        this.inflate();
         BigDecimal lhs = this;
         MathContext workmc = mc;           // working settings
         int mag = Math.abs(n);               // magnitude of n
         if (mc.precision > 0) {
-
             int elength = longDigitLength(mag); // length of n in digits
             if (elength > mc.precision)        // X3.274 rule
                 throw new ArithmeticException("Invalid operation");
@@ -2044,7 +2108,7 @@
                 // else (!seenbit) no point in squaring ONE
         }
         // if negative n, calculate the reciprocal using working precision
-        if (n<0)                          // [hence mc.precision>0]
+        if (n < 0) // [hence mc.precision>0]
             acc=ONE.divide(acc, workmc);
         // round to final precision and strip zeros
         return doRound(acc, mc);
@@ -2083,14 +2147,11 @@
      * @return {@code -this}.
      */
     public BigDecimal negate() {
-        BigDecimal result;
-        if (intCompact != INFLATED)
-            result = BigDecimal.valueOf(-intCompact, scale);
-        else {
-            result = new BigDecimal(intVal.negate(), scale);
-            result.precision = precision;
+        if (intCompact == INFLATED) {
+            return new BigDecimal(intVal.negate(), INFLATED, scale, precision);
+        } else {
+            return valueOf(-intCompact, scale, precision);
         }
-        return result;
     }
 
     /**
@@ -2186,7 +2247,7 @@
             if (s != INFLATED)
                 result = longDigitLength(s);
             else
-                result = bigDigitLength(inflate());
+                result = bigDigitLength(intVal);
             precision = result;
         }
         return result;
@@ -2202,7 +2263,7 @@
      * @since  1.2
      */
     public BigInteger unscaledValue() {
-        return this.inflate();
+        return this.inflated();
     }
 
     // Rounding Modes
@@ -2383,29 +2444,41 @@
         if (newScale == oldScale)        // easy case
             return this;
         if (this.signum() == 0)            // zero can have any scale
-            return BigDecimal.valueOf(0, newScale);
-
-        long rs = this.intCompact;
-        if (newScale > oldScale) {
-            int raise = checkScale((long)newScale - oldScale);
-            BigInteger rb = null;
-            if (rs == INFLATED ||
-                (rs = longMultiplyPowerTen(rs, raise)) == INFLATED)
-                rb = bigMultiplyPowerTen(raise);
-            return new BigDecimal(rb, rs, newScale,
-                                  (precision > 0) ? precision + raise : 0);
+            return zeroValueOf(newScale);
+        if(this.intCompact!=INFLATED) {
+            long rs = this.intCompact;
+            if (newScale > oldScale) {
+                int raise = checkScale((long) newScale - oldScale);
+                if ((rs = longMultiplyPowerTen(rs, raise)) != INFLATED) {
+                    return valueOf(rs,newScale);
+                }
+                BigInteger rb = bigMultiplyPowerTen(raise);
+                return new BigDecimal(rb, INFLATED, newScale, (precision > 0) ? precision + raise : 0);
+            } else {
+                // newScale < oldScale -- drop some digits
+                // Can't predict the precision due to the effect of rounding.
+                int drop = checkScale((long) oldScale - newScale);
+                if (drop < LONG_TEN_POWERS_TABLE.length) {
+                    return divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], newScale, roundingMode, newScale);
+                } else {
+                    return divideAndRound(this.inflated(), bigTenToThe(drop), newScale, roundingMode, newScale);
+                }
+            }
         } else {
-            // newScale < oldScale -- drop some digits
-            // Can't predict the precision due to the effect of rounding.
-            int drop = checkScale((long)oldScale - newScale);
-            if (drop < LONG_TEN_POWERS_TABLE.length)
-                return divideAndRound(rs, this.intVal,
-                                      LONG_TEN_POWERS_TABLE[drop], null,
-                                      newScale, roundingMode, newScale);
-            else
-                return divideAndRound(rs, this.intVal,
-                                      INFLATED, bigTenToThe(drop),
-                                      newScale, roundingMode, newScale);
+            if (newScale > oldScale) {
+                int raise = checkScale((long) newScale - oldScale);
+                BigInteger rb = bigMultiplyPowerTen(this.intVal,raise);
+                return new BigDecimal(rb, INFLATED, newScale, (precision > 0) ? precision + raise : 0);
+            } else {
+                // newScale < oldScale -- drop some digits
+                // Can't predict the precision due to the effect of rounding.
+                int drop = checkScale((long) oldScale - newScale);
+                if (drop < LONG_TEN_POWERS_TABLE.length)
+                    return divideAndRound(this.intVal, LONG_TEN_POWERS_TABLE[drop], newScale, roundingMode,
+                                          newScale);
+                else
+                    return divideAndRound(this.intVal,  bigTenToThe(drop), newScale, roundingMode, newScale);
+            }
         }
     }
 
@@ -2524,10 +2597,11 @@
      * @since 1.5
      */
     public BigDecimal stripTrailingZeros() {
-        this.inflate();
-        BigDecimal result = new BigDecimal(intVal, scale);
-        result.stripZerosToMatchScale(Long.MIN_VALUE);
-        return result;
+        if(intCompact!=INFLATED) {
+            return createAndStripZerosToMatchScale(intCompact, scale, Long.MIN_VALUE);
+        } else {
+            return createAndStripZerosToMatchScale(intVal, scale, Long.MIN_VALUE);
+        }
     }
 
     // Comparison Operations
@@ -2647,7 +2721,7 @@
         } else if (xs != INFLATED)
             return xs == compactValFor(this.intVal);
 
-        return this.inflate().equals(xDec.inflate());
+        return this.inflated().equals(xDec.inflated());
     }
 
     /**
@@ -2872,13 +2946,38 @@
      * @see #toEngineeringString()
      */
     public String toPlainString() {
-        BigDecimal bd = this;
-        if (bd.scale < 0)
-            bd = bd.setScale(0);
-        bd.inflate();
-        if (bd.scale == 0)      // No decimal point
-            return bd.intVal.toString();
-        return bd.getValueString(bd.signum(), bd.intVal.abs().toString(), bd.scale);
+        if(scale==0) {
+            if(intCompact!=INFLATED) {
+                return Long.toString(intCompact);
+            } else {
+                return intVal.toString();
+            }
+        }
+        if(this.scale<0) { // No decimal point
+            if(signum()==0) {
+                return "0";
+            }
+            int tailingZeros = checkScaleNonZero((-(long)scale));
+            StringBuilder buf;
+            if(intCompact!=INFLATED) {
+                buf = new StringBuilder(20+tailingZeros);
+                buf.append(intCompact);
+            } else {
+                String str = intVal.toString();
+                buf = new StringBuilder(str.length()+tailingZeros);
+                buf.append(str);
+            }
+            for (int i = 0; i < tailingZeros; i++)
+                buf.append('0');
+            return buf.toString();
+        }
+        String str ;
+        if(intCompact!=INFLATED) {
+            str = Long.toString(Math.abs(intCompact));
+        } else {
+            str = intVal.abs().toString();
+        }
+        return getValueString(signum(), str, scale);
     }
 
     /* Returns a digit.digit string */
@@ -2922,7 +3021,7 @@
      */
     public BigInteger toBigInteger() {
         // force to an integer, quietly
-        return this.setScale(0, ROUND_DOWN).inflate();
+        return this.setScale(0, ROUND_DOWN).inflated();
     }
 
     /**
@@ -2937,7 +3036,7 @@
      */
     public BigInteger toBigIntegerExact() {
         // round to an integer, with Exception if decimal part non-0
-        return this.setScale(0, ROUND_UNNECESSARY).inflate();
+        return this.setScale(0, ROUND_UNNECESSARY).inflated();
     }
 
     /**
@@ -2990,7 +3089,7 @@
         BigDecimal num = this.setScale(0, ROUND_UNNECESSARY);
         if (num.precision() >= 19) // need to check carefully
             LongOverflow.check(num);
-        return num.inflate().longValue();
+        return num.inflated().longValue();
     }
 
     private static class LongOverflow {
@@ -3001,9 +3100,9 @@
         private static final BigInteger LONGMAX = BigInteger.valueOf(Long.MAX_VALUE);
 
         public static void check(BigDecimal num) {
-            num.inflate();
-            if ((num.intVal.compareTo(LONGMIN) < 0) ||
-                (num.intVal.compareTo(LONGMAX) > 0))
+            BigInteger intVal = num.inflated();
+            if (intVal.compareTo(LONGMIN) < 0 ||
+                intVal.compareTo(LONGMAX) > 0)
                 throw new java.lang.ArithmeticException("Overflow");
         }
     }
@@ -3107,8 +3206,28 @@
      * @return this {@code BigDecimal} converted to a {@code float}.
      */
     public float floatValue(){
-        if (scale == 0 && intCompact != INFLATED)
+        if(intCompact != INFLATED) {
+            if (scale == 0) {
                 return (float)intCompact;
+            } else {
+                /*
+                 * If both intCompact and the scale can be exactly
+                 * represented as float values, perform a single float
+                 * multiply or divide to compute the (properly
+                 * rounded) result.
+                 */
+                if (Math.abs(intCompact) < 1L<<22 ) {
+                    // Don't have too guard against
+                    // Math.abs(MIN_VALUE) because of outer check
+                    // against INFLATED.
+                    if (scale > 0 && scale < float10pow.length) {
+                        return (float)intCompact / float10pow[scale];
+                    } else if (scale < 0 && scale > -float10pow.length) {
+                        return (float)intCompact * float10pow[-scale];
+                    }
+                }
+            }
+        }
         // Somewhat inefficient, but guaranteed to work.
         return Float.parseFloat(this.toString());
     }
@@ -3130,13 +3249,53 @@
      * @return this {@code BigDecimal} converted to a {@code double}.
      */
     public double doubleValue(){
-        if (scale == 0 && intCompact != INFLATED)
-            return (double)intCompact;
+        if(intCompact != INFLATED) {
+            if (scale == 0) {
+                return (double)intCompact;
+            } else {
+                /*
+                 * If both intCompact and the scale can be exactly
+                 * represented as double values, perform a single
+                 * double multiply or divide to compute the (properly
+                 * rounded) result.
+                 */
+                if (Math.abs(intCompact) < 1L<<52 ) {
+                    // Don't have too guard against
+                    // Math.abs(MIN_VALUE) because of outer check
+                    // against INFLATED.
+                    if (scale > 0 && scale < double10pow.length) {
+                        return (double)intCompact / double10pow[scale];
+                    } else if (scale < 0 && scale > -double10pow.length) {
+                        return (double)intCompact * double10pow[-scale];
+                    }
+                }
+            }
+        }
         // Somewhat inefficient, but guaranteed to work.
         return Double.parseDouble(this.toString());
     }
 
     /**
+     * Powers of 10 which can be represented exactly in {@code
+     * double}.
+     */
+    private static final double double10pow[] = {
+        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.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17,
+        1.0e18, 1.0e19, 1.0e20, 1.0e21, 1.0e22
+    };
+
+    /**
+     * Powers of 10 which can be represented exactly in {@code
+     * float}.
+     */
+    private static final float float10pow[] = {
+        1.0e0f, 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f,
+        1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f
+    };
+
+    /**
      * Returns the size of an ulp, a unit in the last place, of this
      * {@code BigDecimal}.  An ulp of a nonzero {@code BigDecimal}
      * value is the positive distance between this value and the
@@ -3151,10 +3310,9 @@
      * @since 1.5
      */
     public BigDecimal ulp() {
-        return BigDecimal.valueOf(1, this.scale());
+        return BigDecimal.valueOf(1, this.scale(), 1);
     }
 
-
     // Private class to build a string representation for BigDecimal object.
     // "StringBuilderHelper" is constructed as a thread local variable so it is
     // thread safe. The StringBuilder field acts as a buffer to hold the temporary
@@ -3268,6 +3426,15 @@
             return (intCompact != INFLATED) ?
                 Long.toString(intCompact):
                 intVal.toString();
+        if (scale == 2  &&
+            intCompact >= 0 && intCompact < Integer.MAX_VALUE) {
+            // currency fast path
+            int lowInt = (int)intCompact % 100;
+            int highInt = (int)intCompact / 100;
+            return (Integer.toString(highInt) + '.' +
+                    StringBuilderHelper.DIGIT_TENS[lowInt] +
+                    StringBuilderHelper.DIGIT_ONES[lowInt]) ;
+        }
 
         StringBuilderHelper sbHelper = threadLocalStringBuilderHelper.get();
         char[] coeff;
@@ -3377,7 +3544,7 @@
         tenpow[0] = '1';
         for (int i = 1; i <= n; i++)
             tenpow[i] = '0';
-        return new BigInteger(tenpow);
+        return new BigInteger(tenpow,1, tenpow.length);
     }
 
     /**
@@ -3433,11 +3600,16 @@
         1000000000000000000L   // 18 / 10^18
     };
 
-    private static volatile BigInteger BIG_TEN_POWERS_TABLE[] = {BigInteger.ONE,
-        BigInteger.valueOf(10),       BigInteger.valueOf(100),
-        BigInteger.valueOf(1000),     BigInteger.valueOf(10000),
-        BigInteger.valueOf(100000),   BigInteger.valueOf(1000000),
-        BigInteger.valueOf(10000000), BigInteger.valueOf(100000000),
+    private static volatile BigInteger BIG_TEN_POWERS_TABLE[] = {
+        BigInteger.ONE,
+        BigInteger.valueOf(10),
+        BigInteger.valueOf(100),
+        BigInteger.valueOf(1000),
+        BigInteger.valueOf(10000),
+        BigInteger.valueOf(100000),
+        BigInteger.valueOf(1000000),
+        BigInteger.valueOf(10000000),
+        BigInteger.valueOf(100000000),
         BigInteger.valueOf(1000000000),
         BigInteger.valueOf(10000000000L),
         BigInteger.valueOf(100000000000L),
@@ -3502,7 +3674,7 @@
      */
     private BigInteger bigMultiplyPowerTen(int n) {
         if (n <= 0)
-            return this.inflate();
+            return this.inflated();
 
         if (intCompact != INFLATED)
             return bigTenToThe(n).multiply(intCompact);
@@ -3511,12 +3683,13 @@
     }
 
     /**
-     * Assign appropriate BigInteger to intVal field if intVal is
+     * Returns appropriate BigInteger from intVal field if intVal is
      * null, i.e. the compact representation is in use.
      */
-    private BigInteger inflate() {
-        if (intVal == null)
-            intVal = BigInteger.valueOf(intCompact);
+    private BigInteger inflated() {
+        if (intVal == null) {
+            return BigInteger.valueOf(intCompact);
+        }
         return intVal;
     }
 
@@ -3543,6 +3716,28 @@
         }
     }
 
+    private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
+    private static final long intCompactOffset;
+    private static final long intValOffset;
+    static {
+        try {
+            intCompactOffset = unsafe.objectFieldOffset
+                (BigDecimal.class.getDeclaredField("intCompact"));
+            intValOffset = unsafe.objectFieldOffset
+                (BigDecimal.class.getDeclaredField("intVal"));
+        } catch (Exception ex) {
+            throw new Error(ex);
+        }
+    }
+
+    private void setIntCompactVolatile(long val) {
+        unsafe.putLongVolatile(this, intCompactOffset, val);
+    }
+
+    private void setIntValVolatile(BigInteger val) {
+        unsafe.putObjectVolatile(this, intValOffset, val);
+    }
+
     /**
      * Reconstitute the {@code BigDecimal} instance from a stream (that is,
      * deserialize it).
@@ -3559,7 +3754,7 @@
             throw new java.io.StreamCorruptedException(message);
         // [all values of scale are now allowed]
         }
-        intCompact = compactValFor(intVal);
+        setIntCompactVolatile(compactValFor(intVal));
     }
 
    /**
@@ -3570,13 +3765,12 @@
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Must inflate to maintain compatible serial form.
-       this.inflate();
-
-       // Write proper fields
+       if (this.intVal == null)
+           this.setIntValVolatile(BigInteger.valueOf(this.intCompact));
+       // Could reset intVal back to null if it has to be set.
        s.defaultWriteObject();
    }
 
-
     /**
      * Returns the length of the absolute value of a {@code long}, in decimal
      * digits.
@@ -3584,36 +3778,29 @@
      * @param x the {@code long}
      * @return the length of the unscaled value, in deciaml digits.
      */
-    private static int longDigitLength(long x) {
+    static int longDigitLength(long x) {
         /*
          * As described in "Bit Twiddling Hacks" by Sean Anderson,
          * (http://graphics.stanford.edu/~seander/bithacks.html)
-         * integer log 10 of x is within 1 of
-         * (1233/4096)* (1 + integer log 2 of x).
-         * The fraction 1233/4096 approximates log10(2). So we first
-         * do a version of log2 (a variant of Long class with
-         * pre-checks and opposite directionality) and then scale and
-         * check against powers table. This is a little simpler in
-         * present context than the version in Hacker's Delight sec
-         * 11-4.  Adding one to bit length allows comparing downward
-         * from the LONG_TEN_POWERS_TABLE that we need anyway.
+         * integer log 10 of x is within 1 of (1233/4096)* (1 +
+         * integer log 2 of x). The fraction 1233/4096 approximates
+         * log10(2). So we first do a version of log2 (a variant of
+         * Long class with pre-checks and opposite directionality) and
+         * then scale and check against powers table. This is a little
+         * simpler in present context than the version in Hacker's
+         * Delight sec 11-4. Adding one to bit length allows comparing
+         * downward from the LONG_TEN_POWERS_TABLE that we need
+         * anyway.
          */
-        assert x != INFLATED;
+        assert x != BigDecimal.INFLATED;
         if (x < 0)
             x = -x;
         if (x < 10) // must screen for 0, might as well 10
             return 1;
-        int n = 64; // not 63, to avoid needing to add 1 later
-        int y = (int)(x >>> 32);
-        if (y == 0) { n -= 32; y = (int)x; }
-        if (y >>> 16 == 0) { n -= 16; y <<= 16; }
-        if (y >>> 24 == 0) { n -=  8; y <<=  8; }
-        if (y >>> 28 == 0) { n -=  4; y <<=  4; }
-        if (y >>> 30 == 0) { n -=  2; y <<=  2; }
-        int r = (((y >>> 31) + n) * 1233) >>> 12;
+        int r = ((64 - Long.numberOfLeadingZeros(x) + 1) * 1233) >>> 12;
         long[] tab = LONG_TEN_POWERS_TABLE;
         // if r >= length, must have max possible digits for long
-        return (r >= tab.length || x < tab[r])? r : r+1;
+        return (r >= tab.length || x < tab[r]) ? r : r + 1;
     }
 
     /**
@@ -3635,41 +3822,6 @@
         return b.compareMagnitude(bigTenToThe(r)) < 0? r : r+1;
     }
 
-
-    /**
-     * Remove insignificant trailing zeros from this
-     * {@code BigDecimal} until the preferred scale is reached or no
-     * more zeros can be removed.  If the preferred scale is less than
-     * Integer.MIN_VALUE, all the trailing zeros will be removed.
-     *
-     * {@code BigInteger} assistance could help, here?
-     *
-     * <p>WARNING: This method should only be called on new objects as
-     * it mutates the value fields.
-     *
-     * @return this {@code BigDecimal} with a scale possibly reduced
-     * to be closed to the preferred scale.
-     */
-    private BigDecimal stripZerosToMatchScale(long preferredScale) {
-        this.inflate();
-        BigInteger qr[];                // quotient-remainder pair
-        while ( intVal.compareMagnitude(BigInteger.TEN) >= 0 &&
-                scale > preferredScale) {
-            if (intVal.testBit(0))
-                break;                  // odd number cannot end in 0
-            qr = intVal.divideAndRemainder(BigInteger.TEN);
-            if (qr[1].signum() != 0)
-                break;                  // non-0 remainder
-            intVal=qr[0];
-            scale = checkScale((long)scale-1);  // could Overflow
-            if (precision > 0)          // adjust precision if known
-              precision--;
-        }
-        if (intVal != null)
-            intCompact = compactValFor(intVal);
-        return this;
-    }
-
     /**
      * Check a scale for Underflow or Overflow.  If this BigDecimal is
      * nonzero, throw an exception if the scale is outof range. If this
@@ -3693,74 +3845,7 @@
         return asInt;
     }
 
-    /**
-     * Round an operand; used only if digits &gt; 0.  Does not change
-     * {@code this}; if rounding is needed a new {@code BigDecimal}
-     * is created and returned.
-     *
-     * @param mc the context to use.
-     * @throws ArithmeticException if the result is inexact but the
-     *         rounding mode is {@code UNNECESSARY}.
-     */
-    private BigDecimal roundOp(MathContext mc) {
-        BigDecimal rounded = doRound(this, mc);
-        return rounded;
-    }
-
-    /** Round this BigDecimal according to the MathContext settings;
-     *  used only if precision {@literal >} 0.
-     *
-     * <p>WARNING: This method should only be called on new objects as
-     * it mutates the value fields.
-     *
-     * @param mc the context to use.
-     * @throws ArithmeticException if the rounding mode is
-     *         {@code RoundingMode.UNNECESSARY} and the
-     *         {@code BigDecimal} operation would require rounding.
-     */
-    private void roundThis(MathContext mc) {
-        BigDecimal rounded = doRound(this, mc);
-        if (rounded == this)                 // wasn't rounded
-            return;
-        this.intVal     = rounded.intVal;
-        this.intCompact = rounded.intCompact;
-        this.scale      = rounded.scale;
-        this.precision  = rounded.precision;
-    }
-
-    /**
-     * Returns a {@code BigDecimal} rounded according to the
-     * MathContext settings; used only if {@code mc.precision > 0}.
-     * Does not change {@code this}; if rounding is needed a new
-     * {@code BigDecimal} is created and returned.
-     *
-     * @param mc the context to use.
-     * @return a {@code BigDecimal} rounded according to the MathContext
-     *         settings.  May return this, if no rounding needed.
-     * @throws ArithmeticException if the rounding mode is
-     *         {@code RoundingMode.UNNECESSARY} and the
-     *         result is inexact.
-     */
-    private static BigDecimal doRound(BigDecimal d, MathContext mc) {
-        int mcp = mc.precision;
-        int drop;
-        // This might (rarely) iterate to cover the 999=>1000 case
-        while ((drop = d.precision() - mcp) > 0) {
-            int newScale = d.checkScale((long)d.scale - drop);
-            int mode = mc.roundingMode.oldMode;
-            if (drop < LONG_TEN_POWERS_TABLE.length)
-                d = divideAndRound(d.intCompact, d.intVal,
-                                   LONG_TEN_POWERS_TABLE[drop], null,
-                                   newScale, mode, newScale);
-            else
-                d = divideAndRound(d.intCompact, d.intVal,
-                                   INFLATED, bigTenToThe(drop),
-                                   newScale, mode, newScale);
-        }
-        return d;
-    }
-
-    /**
+   /**
      * Returns the compact value for given {@code BigInteger}, or
      * INFLATED if too big. Relies on internal representation of
      * {@code BigInteger}.
@@ -3852,4 +3937,1275 @@
         }
         return this;
     }
+
+    /* the same as checkScale where value!=0 */
+    private static int checkScaleNonZero(long val) {
+        int asInt = (int)val;
+        if (asInt != val) {
+            throw new ArithmeticException(asInt>0 ? "Underflow":"Overflow");
+        }
+        return asInt;
+    }
+
+    private static int checkScale(long intCompact, long val) {
+        int asInt = (int)val;
+        if (asInt != val) {
+            asInt = val>Integer.MAX_VALUE ? Integer.MAX_VALUE : Integer.MIN_VALUE;
+            if (intCompact != 0)
+                throw new ArithmeticException(asInt>0 ? "Underflow":"Overflow");
+        }
+        return asInt;
+    }
+
+    private static int checkScale(BigInteger intVal, long val) {
+        int asInt = (int)val;
+        if (asInt != val) {
+            asInt = val>Integer.MAX_VALUE ? Integer.MAX_VALUE : Integer.MIN_VALUE;
+            if (intVal.signum() != 0)
+                throw new ArithmeticException(asInt>0 ? "Underflow":"Overflow");
+        }
+        return asInt;
+    }
+
+    /**
+     * Returns a {@code BigDecimal} rounded according to the MathContext
+     * settings;
+     * If rounding is needed a new {@code BigDecimal} is created and returned.
+     *
+     * @param val the value to be rounded
+     * @param mc the context to use.
+     * @return a {@code BigDecimal} rounded according to the MathContext
+     *         settings.  May return {@code value}, if no rounding needed.
+     * @throws ArithmeticException if the rounding mode is
+     *         {@code RoundingMode.UNNECESSARY} and the
+     *         result is inexact.
+     */
+    private static BigDecimal doRound(BigDecimal val, MathContext mc) {
+        int mcp = mc.precision;
+        boolean wasDivided = false;
+        if (mcp > 0) {
+            BigInteger intVal = val.intVal;
+            long compactVal = val.intCompact;
+            int scale = val.scale;
+            int prec = val.precision();
+            int mode = mc.roundingMode.oldMode;
+            int drop;
+            if (compactVal == INFLATED) {
+                drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    intVal = divideAndRoundByTenPow(intVal, drop, mode);
+                    wasDivided = true;
+                    compactVal = compactValFor(intVal);
+                    if (compactVal != INFLATED) {
+                        prec = longDigitLength(compactVal);
+                        break;
+                    }
+                    prec = bigDigitLength(intVal);
+                    drop = prec - mcp;
+                }
+            }
+            if (compactVal != INFLATED) {
+                drop = prec - mcp;  // drop can't be more than 18
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                    wasDivided = true;
+                    prec = longDigitLength(compactVal);
+                    drop = prec - mcp;
+                    intVal = null;
+                }
+            }
+            return wasDivided ? new BigDecimal(intVal,compactVal,scale,prec) : val;
+        }
+        return val;
+    }
+
+    /*
+     * Returns a {@code BigDecimal} created from {@code long} value with
+     * given scale rounded according to the MathContext settings
+     */
+    private static BigDecimal doRound(long compactVal, int scale, MathContext mc) {
+        int mcp = mc.precision;
+        if (mcp > 0 && mcp < 19) {
+            int prec = longDigitLength(compactVal);
+            int drop = prec - mcp;  // drop can't be more than 18
+            while (drop > 0) {
+                scale = checkScaleNonZero((long) scale - drop);
+                compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                prec = longDigitLength(compactVal);
+                drop = prec - mcp;
+            }
+            return valueOf(compactVal, scale, prec);
+        }
+        return valueOf(compactVal, scale);
+    }
+
+    /*
+     * Returns a {@code BigDecimal} created from {@code BigInteger} value with
+     * given scale rounded according to the MathContext settings
+     */
+    private static BigDecimal doRound(BigInteger intVal, int scale, MathContext mc) {
+        int mcp = mc.precision;
+        int prec = 0;
+        if (mcp > 0) {
+            long compactVal = compactValFor(intVal);
+            int mode = mc.roundingMode.oldMode;
+            int drop;
+            if (compactVal == INFLATED) {
+                prec = bigDigitLength(intVal);
+                drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    intVal = divideAndRoundByTenPow(intVal, drop, mode);
+                    compactVal = compactValFor(intVal);
+                    if (compactVal != INFLATED) {
+                        break;
+                    }
+                    prec = bigDigitLength(intVal);
+                    drop = prec - mcp;
+                }
+            }
+            if (compactVal != INFLATED) {
+                prec = longDigitLength(compactVal);
+                drop = prec - mcp;     // drop can't be more than 18
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                    prec = longDigitLength(compactVal);
+                    drop = prec - mcp;
+                }
+                return valueOf(compactVal,scale,prec);
+            }
+        }
+        return new BigDecimal(intVal,INFLATED,scale,prec);
+    }
+
+    /*
+     * Divides {@code BigInteger} value by ten power.
+     */
+    private static BigInteger divideAndRoundByTenPow(BigInteger intVal, int tenPow, int roundingMode) {
+        if (tenPow < LONG_TEN_POWERS_TABLE.length)
+            intVal = divideAndRound(intVal, LONG_TEN_POWERS_TABLE[tenPow], roundingMode);
+        else
+            intVal = divideAndRound(intVal, bigTenToThe(tenPow), roundingMode);
+        return intVal;
+    }
+
+    /**
+     * Internally used for division operation for division {@code long} by
+     * {@code long}.
+     * The returned {@code BigDecimal} object is the quotient whose scale is set
+     * to the passed in scale. If the remainder is not zero, it will be rounded
+     * based on the passed in roundingMode. Also, if the remainder is zero and
+     * the last parameter, i.e. preferredScale is NOT equal to scale, the
+     * trailing zeros of the result is stripped to match the preferredScale.
+     */
+    private static BigDecimal divideAndRound(long ldividend, long ldivisor, int scale, int roundingMode,
+                                             int preferredScale) {
+
+        int qsign; // quotient sign
+        long q = ldividend / ldivisor; // store quotient in long
+        if (roundingMode == ROUND_DOWN && scale == preferredScale)
+            return valueOf(q, scale);
+        long r = ldividend % ldivisor; // store remainder in long
+        qsign = ((ldividend < 0) == (ldivisor < 0)) ? 1 : -1;
+        if (r != 0) {
+            boolean increment = needIncrement(ldivisor, roundingMode, qsign, q, r);
+            return valueOf((increment ? q + qsign : q), scale);
+        } else {
+            if (preferredScale != scale)
+                return createAndStripZerosToMatchScale(q, scale, preferredScale);
+            else
+                return valueOf(q, scale);
+        }
+    }
+
+    /**
+     * Divides {@code long} by {@code long} and do rounding based on the
+     * passed in roundingMode.
+     */
+    private static long divideAndRound(long ldividend, long ldivisor, int roundingMode) {
+        int qsign; // quotient sign
+        long q = ldividend / ldivisor; // store quotient in long
+        if (roundingMode == ROUND_DOWN)
+            return q;
+        long r = ldividend % ldivisor; // store remainder in long
+        qsign = ((ldividend < 0) == (ldivisor < 0)) ? 1 : -1;
+        if (r != 0) {
+            boolean increment = needIncrement(ldivisor, roundingMode, qsign, q,     r);
+            return increment ? q + qsign : q;
+        } else {
+            return q;
+        }
+    }
+
+    /**
+     * Shared logic of need increment computation.
+     */
+    private static boolean commonNeedIncrement(int roundingMode, int qsign,
+                                        int cmpFracHalf, boolean oddQuot) {
+        switch(roundingMode) {
+        case ROUND_UNNECESSARY:
+            throw new ArithmeticException("Rounding necessary");
+
+        case ROUND_UP: // Away from zero
+            return true;
+
+        case ROUND_DOWN: // Towards zero
+            return false;
+
+        case ROUND_CEILING: // Towards +infinity
+            return qsign > 0;
+
+        case ROUND_FLOOR: // Towards -infinity
+            return qsign < 0;
+
+        default: // Some kind of half-way rounding
+            if (roundingMode == ROUND_HALF_DOWN ||
+                cmpFracHalf < 0 ) // We're closer to higher digit
+                return false;
+            else if (roundingMode == ROUND_HALF_UP ||
+                     cmpFracHalf > 0 ) // We're closer to lower digit
+                return true;
+            else
+                // roundingMode == ROUND_HALF_EVEN, true iff quotient is odd
+                return oddQuot;
+        }
+    }
+
+    /**
+     * Tests if quotient has to be incremented according the roundingMode
+     */
+    private static boolean needIncrement(long ldivisor, int roundingMode,
+                                         int qsign, long q, long r) {
+        assert r != 0L;
+
+        int cmpFracHalf;
+        if (r <= HALF_LONG_MIN_VALUE || r > HALF_LONG_MAX_VALUE) {
+            cmpFracHalf = 1; // 2 * r can't fit into long
+        } else {
+            cmpFracHalf = longCompareMagnitude(2 * r, ldivisor);
+        }
+
+        return commonNeedIncrement(roundingMode, qsign, cmpFracHalf, (q & 1L) != 0L);
+    }
+
+    /**
+     * Divides {@code BigInteger} value by {@code long} value and
+     * do rounding based on the passed in roundingMode.
+     */
+    private static BigInteger divideAndRound(BigInteger bdividend, long ldivisor, int roundingMode) {
+        boolean isRemainderZero; // record remainder is zero or not
+        int qsign; // quotient sign
+        long r = 0; // store quotient & remainder in long
+        MutableBigInteger mq = null; // store quotient
+        // Descend into mutables for faster remainder checks
+        MutableBigInteger mdividend = new MutableBigInteger(bdividend.mag);
+        mq = new MutableBigInteger();
+        r = mdividend.divide(ldivisor, mq);
+        isRemainderZero = (r == 0);
+        qsign = (ldivisor < 0) ? -bdividend.signum : bdividend.signum;
+        if (!isRemainderZero) {
+            if(needIncrement(ldivisor, roundingMode, qsign, mq, r)) {
+                mq.add(MutableBigInteger.ONE);
+            }
+        }
+        return mq.toBigInteger(qsign);
+    }
+
+    /**
+     * Internally used for division operation for division {@code BigInteger}
+     * by {@code long}.
+     * The returned {@code BigDecimal} object is the quotient whose scale is set
+     * to the passed in scale. If the remainder is not zero, it will be rounded
+     * based on the passed in roundingMode. Also, if the remainder is zero and
+     * the last parameter, i.e. preferredScale is NOT equal to scale, the
+     * trailing zeros of the result is stripped to match the preferredScale.
+     */
+    private static BigDecimal divideAndRound(BigInteger bdividend,
+                                             long ldivisor, int scale, int roundingMode, int preferredScale) {
+        boolean isRemainderZero; // record remainder is zero or not
+        int qsign; // quotient sign
+        long r = 0; // store quotient & remainder in long
+        MutableBigInteger mq = null; // store quotient
+        // Descend into mutables for faster remainder checks
+        MutableBigInteger mdividend = new MutableBigInteger(bdividend.mag);
+        mq = new MutableBigInteger();
+        r = mdividend.divide(ldivisor, mq);
+        isRemainderZero = (r == 0);
+        qsign = (ldivisor < 0) ? -bdividend.signum : bdividend.signum;
+        if (!isRemainderZero) {
+            if(needIncrement(ldivisor, roundingMode, qsign, mq, r)) {
+                mq.add(MutableBigInteger.ONE);
+            }
+            return mq.toBigDecimal(qsign, scale);
+        } else {
+            if (preferredScale != scale) {
+                long compactVal = mq.toCompactValue(qsign);
+                if(compactVal!=INFLATED) {
+                    return createAndStripZerosToMatchScale(compactVal, scale, preferredScale);
+                }
+                BigInteger intVal =  mq.toBigInteger(qsign);
+                return createAndStripZerosToMatchScale(intVal,scale, preferredScale);
+            } else {
+                return mq.toBigDecimal(qsign, scale);
+            }
+        }
+    }
+
+    /**
+     * Tests if quotient has to be incremented according the roundingMode
+     */
+    private static boolean needIncrement(long ldivisor, int roundingMode,
+                                         int qsign, MutableBigInteger mq, long r) {
+        assert r != 0L;
+
+        int cmpFracHalf;
+        if (r <= HALF_LONG_MIN_VALUE || r > HALF_LONG_MAX_VALUE) {
+            cmpFracHalf = 1; // 2 * r can't fit into long
+        } else {
+            cmpFracHalf = longCompareMagnitude(2 * r, ldivisor);
+        }
+
+        return commonNeedIncrement(roundingMode, qsign, cmpFracHalf, mq.isOdd());
+    }
+
+    /**
+     * Divides {@code BigInteger} value by {@code BigInteger} value and
+     * do rounding based on the passed in roundingMode.
+     */
+    private static BigInteger divideAndRound(BigInteger bdividend, BigInteger bdivisor, int roundingMode) {
+        boolean isRemainderZero; // record remainder is zero or not
+        int qsign; // quotient sign
+        // Descend into mutables for faster remainder checks
+        MutableBigInteger mdividend = new MutableBigInteger(bdividend.mag);
+        MutableBigInteger mq = new MutableBigInteger();
+        MutableBigInteger mdivisor = new MutableBigInteger(bdivisor.mag);
+        MutableBigInteger mr = mdividend.divide(mdivisor, mq);
+        isRemainderZero = mr.isZero();
+        qsign = (bdividend.signum != bdivisor.signum) ? -1 : 1;
+        if (!isRemainderZero) {
+            if (needIncrement(mdivisor, roundingMode, qsign, mq, mr)) {
+                mq.add(MutableBigInteger.ONE);
+            }
+        }
+        return mq.toBigInteger(qsign);
+    }
+
+    /**
+     * Internally used for division operation for division {@code BigInteger}
+     * by {@code BigInteger}.
+     * The returned {@code BigDecimal} object is the quotient whose scale is set
+     * to the passed in scale. If the remainder is not zero, it will be rounded
+     * based on the passed in roundingMode. Also, if the remainder is zero and
+     * the last parameter, i.e. preferredScale is NOT equal to scale, the
+     * trailing zeros of the result is stripped to match the preferredScale.
+     */
+    private static BigDecimal divideAndRound(BigInteger bdividend, BigInteger bdivisor, int scale, int roundingMode,
+                                             int preferredScale) {
+        boolean isRemainderZero; // record remainder is zero or not
+        int qsign; // quotient sign
+        // Descend into mutables for faster remainder checks
+        MutableBigInteger mdividend = new MutableBigInteger(bdividend.mag);
+        MutableBigInteger mq = new MutableBigInteger();
+        MutableBigInteger mdivisor = new MutableBigInteger(bdivisor.mag);
+        MutableBigInteger mr = mdividend.divide(mdivisor, mq);
+        isRemainderZero = mr.isZero();
+        qsign = (bdividend.signum != bdivisor.signum) ? -1 : 1;
+        if (!isRemainderZero) {
+            if (needIncrement(mdivisor, roundingMode, qsign, mq, mr)) {
+                mq.add(MutableBigInteger.ONE);
+            }
+            return mq.toBigDecimal(qsign, scale);
+        } else {
+            if (preferredScale != scale) {
+                long compactVal = mq.toCompactValue(qsign);
+                if (compactVal != INFLATED) {
+                    return createAndStripZerosToMatchScale(compactVal, scale, preferredScale);
+                }
+                BigInteger intVal = mq.toBigInteger(qsign);
+                return createAndStripZerosToMatchScale(intVal, scale, preferredScale);
+            } else {
+                return mq.toBigDecimal(qsign, scale);
+            }
+        }
+    }
+
+    /**
+     * Tests if quotient has to be incremented according the roundingMode
+     */
+    private static boolean needIncrement(MutableBigInteger mdivisor, int roundingMode,
+                                         int qsign, MutableBigInteger mq, MutableBigInteger mr) {
+        assert !mr.isZero();
+        int cmpFracHalf = mr.compareHalf(mdivisor);
+        return commonNeedIncrement(roundingMode, qsign, cmpFracHalf, mq.isOdd());
+    }
+
+    /**
+     * Remove insignificant trailing zeros from this
+     * {@code BigInteger} value until the preferred scale is reached or no
+     * more zeros can be removed.  If the preferred scale is less than
+     * Integer.MIN_VALUE, all the trailing zeros will be removed.
+     *
+     * @return new {@code BigDecimal} with a scale possibly reduced
+     * to be closed to the preferred scale.
+     */
+    private static BigDecimal createAndStripZerosToMatchScale(BigInteger intVal, int scale, long preferredScale) {
+        BigInteger qr[]; // quotient-remainder pair
+        while (intVal.compareMagnitude(BigInteger.TEN) >= 0
+               && scale > preferredScale) {
+            if (intVal.testBit(0))
+                break; // odd number cannot end in 0
+            qr = intVal.divideAndRemainder(BigInteger.TEN);
+            if (qr[1].signum() != 0)
+                break; // non-0 remainder
+            intVal = qr[0];
+            scale = checkScale(intVal,(long) scale - 1); // could Overflow
+        }
+        return valueOf(intVal, scale, 0);
+    }
+
+    /**
+     * Remove insignificant trailing zeros from this
+     * {@code long} value until the preferred scale is reached or no
+     * more zeros can be removed.  If the preferred scale is less than
+     * Integer.MIN_VALUE, all the trailing zeros will be removed.
+     *
+     * @return new {@code BigDecimal} with a scale possibly reduced
+     * to be closed to the preferred scale.
+     */
+    private static BigDecimal createAndStripZerosToMatchScale(long compactVal, int scale, long preferredScale) {
+        while (Math.abs(compactVal) >= 10L && scale > preferredScale) {
+            if ((compactVal & 1L) != 0L)
+                break; // odd number cannot end in 0
+            long r = compactVal % 10L;
+            if (r != 0L)
+                break; // non-0 remainder
+            compactVal /= 10;
+            scale = checkScale(compactVal, (long) scale - 1); // could Overflow
+        }
+        return valueOf(compactVal, scale);
+    }
+
+    private static BigDecimal stripZerosToMatchScale(BigInteger intVal, long intCompact, int scale, int preferredScale) {
+        if(intCompact!=INFLATED) {
+            return createAndStripZerosToMatchScale(intCompact, scale, preferredScale);
+        } else {
+            return createAndStripZerosToMatchScale(intVal==null ? INFLATED_BIGINT : intVal,
+                                                   scale, preferredScale);
+        }
+    }
+
+    /*
+     * returns INFLATED if oveflow
+     */
+    private static long add(long xs, long ys){
+        long sum = xs + ys;
+        // See "Hacker's Delight" section 2-12 for explanation of
+        // the overflow test.
+        if ( (((sum ^ xs) & (sum ^ ys))) >= 0L) { // not overflowed
+            return sum;
+        }
+        return INFLATED;
+    }
+
+    private static BigDecimal add(long xs, long ys, int scale){
+        long sum = add(xs, ys);
+        if (sum!=INFLATED)
+            return BigDecimal.valueOf(sum, scale);
+        return new BigDecimal(BigInteger.valueOf(xs).add(ys), scale);
+    }
+
+    private static BigDecimal add(final long xs, int scale1, final long ys, int scale2) {
+        long sdiff = (long) scale1 - scale2;
+        if (sdiff == 0) {
+            return add(xs, ys, scale1);
+        } else if (sdiff < 0) {
+            int raise = checkScale(xs,-sdiff);
+            long scaledX = longMultiplyPowerTen(xs, raise);
+            if (scaledX != INFLATED) {
+                return add(scaledX, ys, scale2);
+            } else {
+                BigInteger bigsum = bigMultiplyPowerTen(xs,raise).add(ys);
+                return ((xs^ys)>=0) ? // same sign test
+                    new BigDecimal(bigsum, INFLATED, scale2, 0)
+                    : valueOf(bigsum, scale2, 0);
+            }
+        } else {
+            int raise = checkScale(ys,sdiff);
+            long scaledY = longMultiplyPowerTen(ys, raise);
+            if (scaledY != INFLATED) {
+                return add(xs, scaledY, scale1);
+            } else {
+                BigInteger bigsum = bigMultiplyPowerTen(ys,raise).add(xs);
+                return ((xs^ys)>=0) ?
+                    new BigDecimal(bigsum, INFLATED, scale1, 0)
+                    : valueOf(bigsum, scale1, 0);
+            }
+        }
+    }
+
+    private static BigDecimal add(final long xs, int scale1, BigInteger snd, int scale2) {
+        int rscale = scale1;
+        long sdiff = (long)rscale - scale2;
+        boolean sameSigns =  (Long.signum(xs) == snd.signum);
+        BigInteger sum;
+        if (sdiff < 0) {
+            int raise = checkScale(xs,-sdiff);
+            rscale = scale2;
+            long scaledX = longMultiplyPowerTen(xs, raise);
+            if (scaledX == INFLATED) {
+                sum = snd.add(bigMultiplyPowerTen(xs,raise));
+            } else {
+                sum = snd.add(scaledX);
+            }
+        } else { //if (sdiff > 0) {
+            int raise = checkScale(snd,sdiff);
+            snd = bigMultiplyPowerTen(snd,raise);
+            sum = snd.add(xs);
+        }
+        return (sameSigns) ?
+            new BigDecimal(sum, INFLATED, rscale, 0) :
+            valueOf(sum, rscale, 0);
+    }
+
+    private static BigDecimal add(BigInteger fst, int scale1, BigInteger snd, int scale2) {
+        int rscale = scale1;
+        long sdiff = (long)rscale - scale2;
+        if (sdiff != 0) {
+            if (sdiff < 0) {
+                int raise = checkScale(fst,-sdiff);
+                rscale = scale2;
+                fst = bigMultiplyPowerTen(fst,raise);
+            } else {
+                int raise = checkScale(snd,sdiff);
+                snd = bigMultiplyPowerTen(snd,raise);
+            }
+        }
+        BigInteger sum = fst.add(snd);
+        return (fst.signum == snd.signum) ?
+                new BigDecimal(sum, INFLATED, rscale, 0) :
+                valueOf(sum, rscale, 0);
+    }
+
+    private static BigInteger bigMultiplyPowerTen(long value, int n) {
+        if (n <= 0)
+            return BigInteger.valueOf(value);
+        return bigTenToThe(n).multiply(value);
+    }
+
+    private static BigInteger bigMultiplyPowerTen(BigInteger value, int n) {
+        if (n <= 0)
+            return value;
+        if(n<LONG_TEN_POWERS_TABLE.length) {
+                return value.multiply(LONG_TEN_POWERS_TABLE[n]);
+        }
+        return value.multiply(bigTenToThe(n));
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (xs /
+     * ys)}, with rounding according to the context settings.
+     *
+     * Fast path - used only when (xscale <= yscale && yscale < 18
+     *  && mc.presision<18) {
+     */
+    private static BigDecimal divideSmallFastPath(final long xs, int xscale,
+                                                  final long ys, int yscale,
+                                                  long preferredScale, MathContext mc) {
+        int mcp = mc.precision;
+        int roundingMode = mc.roundingMode.oldMode;
+
+        assert (xscale <= yscale) && (yscale < 18) && (mcp < 18);
+        int xraise = yscale - xscale; // xraise >=0
+        long scaledX = (xraise==0) ? xs :
+            longMultiplyPowerTen(xs, xraise); // can't overflow here!
+        BigDecimal quotient;
+
+        int cmp = longCompareMagnitude(scaledX, ys);
+        if(cmp > 0) { // satisfy constraint (b)
+            yscale -= 1; // [that is, divisor *= 10]
+            int scl = checkScaleNonZero(preferredScale + yscale - xscale + mcp);
+            if (checkScaleNonZero((long) mcp + yscale) > xscale) {
+                // assert newScale >= xscale
+                int raise = checkScaleNonZero((long) mcp + yscale - xscale);
+                long scaledXs;
+                if ((scaledXs = longMultiplyPowerTen(xs, raise)) == INFLATED) {
+                    quotient = null;
+                    if((mcp-1) >=0 && (mcp-1)<LONG_TEN_POWERS_TABLE.length) {
+                        quotient = multiplyDivideAndRound(LONG_TEN_POWERS_TABLE[mcp-1], scaledX, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+                    }
+                    if(quotient==null) {
+                        BigInteger rb = bigMultiplyPowerTen(scaledX,mcp-1);
+                        quotient = divideAndRound(rb, ys,
+                                                  scl, roundingMode, checkScaleNonZero(preferredScale));
+                    }
+                } else {
+                    quotient = divideAndRound(scaledXs, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+                }
+            } else {
+                int newScale = checkScaleNonZero((long) xscale - mcp);
+                // assert newScale >= yscale
+                if (newScale == yscale) { // easy case
+                    quotient = divideAndRound(xs, ys, scl, roundingMode,checkScaleNonZero(preferredScale));
+                } else {
+                    int raise = checkScaleNonZero((long) newScale - yscale);
+                    long scaledYs;
+                    if ((scaledYs = longMultiplyPowerTen(ys, raise)) == INFLATED) {
+                        BigInteger rb = bigMultiplyPowerTen(ys,raise);
+                        quotient = divideAndRound(BigInteger.valueOf(xs),
+                                                  rb, scl, roundingMode,checkScaleNonZero(preferredScale));
+                    } else {
+                        quotient = divideAndRound(xs, scaledYs, scl, roundingMode,checkScaleNonZero(preferredScale));
+                    }
+                }
+            }
+        } else {
+            // abs(scaledX) <= abs(ys)
+            // result is "scaledX * 10^msp / ys"
+            int scl = checkScaleNonZero(preferredScale + yscale - xscale + mcp);
+            if(cmp==0) {
+                // abs(scaleX)== abs(ys) => result will be scaled 10^mcp + correct sign
+                quotient = roundedTenPower(((scaledX < 0) == (ys < 0)) ? 1 : -1, mcp, scl, checkScaleNonZero(preferredScale));
+            } else {
+                // abs(scaledX) < abs(ys)
+                long scaledXs;
+                if ((scaledXs = longMultiplyPowerTen(scaledX, mcp)) == INFLATED) {
+                    quotient = null;
+                    if(mcp<LONG_TEN_POWERS_TABLE.length) {
+                        quotient = multiplyDivideAndRound(LONG_TEN_POWERS_TABLE[mcp], scaledX, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+                    }
+                    if(quotient==null) {
+                        BigInteger rb = bigMultiplyPowerTen(scaledX,mcp);
+                        quotient = divideAndRound(rb, ys,
+                                                  scl, roundingMode, checkScaleNonZero(preferredScale));
+                    }
+                } else {
+                    quotient = divideAndRound(scaledXs, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+                }
+            }
+        }
+        // doRound, here, only affects 1000000000 case.
+        return doRound(quotient,mc);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (xs /
+     * ys)}, with rounding according to the context settings.
+     */
+    private static BigDecimal divide(final long xs, int xscale, final long ys, int yscale, long preferredScale, MathContext mc) {
+        int mcp = mc.precision;
+        if(xscale <= yscale && yscale < 18 && mcp<18) {
+            return divideSmallFastPath(xs, xscale, ys, yscale, preferredScale, mc);
+        }
+        if (compareMagnitudeNormalized(xs, xscale, ys, yscale) > 0) {// satisfy constraint (b)
+            yscale -= 1; // [that is, divisor *= 10]
+        }
+        int roundingMode = mc.roundingMode.oldMode;
+        // In order to find out whether the divide generates the exact result,
+        // we avoid calling the above divide method. 'quotient' holds the
+        // return BigDecimal object whose scale will be set to 'scl'.
+        int scl = checkScaleNonZero(preferredScale + yscale - xscale + mcp);
+        BigDecimal quotient;
+        if (checkScaleNonZero((long) mcp + yscale) > xscale) {
+            int raise = checkScaleNonZero((long) mcp + yscale - xscale);
+            long scaledXs;
+            if ((scaledXs = longMultiplyPowerTen(xs, raise)) == INFLATED) {
+                BigInteger rb = bigMultiplyPowerTen(xs,raise);
+                quotient = divideAndRound(rb, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+            } else {
+                quotient = divideAndRound(scaledXs, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+            }
+        } else {
+            int newScale = checkScaleNonZero((long) xscale - mcp);
+            // assert newScale >= yscale
+            if (newScale == yscale) { // easy case
+                quotient = divideAndRound(xs, ys, scl, roundingMode,checkScaleNonZero(preferredScale));
+            } else {
+                int raise = checkScaleNonZero((long) newScale - yscale);
+                long scaledYs;
+                if ((scaledYs = longMultiplyPowerTen(ys, raise)) == INFLATED) {
+                    BigInteger rb = bigMultiplyPowerTen(ys,raise);
+                    quotient = divideAndRound(BigInteger.valueOf(xs),
+                                              rb, scl, roundingMode,checkScaleNonZero(preferredScale));
+                } else {
+                    quotient = divideAndRound(xs, scaledYs, scl, roundingMode,checkScaleNonZero(preferredScale));
+                }
+            }
+        }
+        // doRound, here, only affects 1000000000 case.
+        return doRound(quotient,mc);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (xs /
+     * ys)}, with rounding according to the context settings.
+     */
+    private static BigDecimal divide(BigInteger xs, int xscale, long ys, int yscale, long preferredScale, MathContext mc) {
+        // Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+        if ((-compareMagnitudeNormalized(ys, yscale, xs, xscale)) > 0) {// satisfy constraint (b)
+            yscale -= 1; // [that is, divisor *= 10]
+        }
+        int mcp = mc.precision;
+        int roundingMode = mc.roundingMode.oldMode;
+
+        // In order to find out whether the divide generates the exact result,
+        // we avoid calling the above divide method. 'quotient' holds the
+        // return BigDecimal object whose scale will be set to 'scl'.
+        BigDecimal quotient;
+        int scl = checkScaleNonZero(preferredScale + yscale - xscale + mcp);
+        if (checkScaleNonZero((long) mcp + yscale) > xscale) {
+            int raise = checkScaleNonZero((long) mcp + yscale - xscale);
+            BigInteger rb = bigMultiplyPowerTen(xs,raise);
+            quotient = divideAndRound(rb, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+        } else {
+            int newScale = checkScaleNonZero((long) xscale - mcp);
+            // assert newScale >= yscale
+            if (newScale == yscale) { // easy case
+                quotient = divideAndRound(xs, ys, scl, roundingMode,checkScaleNonZero(preferredScale));
+            } else {
+                int raise = checkScaleNonZero((long) newScale - yscale);
+                long scaledYs;
+                if ((scaledYs = longMultiplyPowerTen(ys, raise)) == INFLATED) {
+                    BigInteger rb = bigMultiplyPowerTen(ys,raise);
+                    quotient = divideAndRound(xs, rb, scl, roundingMode,checkScaleNonZero(preferredScale));
+                } else {
+                    quotient = divideAndRound(xs, scaledYs, scl, roundingMode,checkScaleNonZero(preferredScale));
+                }
+            }
+        }
+        // doRound, here, only affects 1000000000 case.
+        return doRound(quotient, mc);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (xs /
+     * ys)}, with rounding according to the context settings.
+     */
+    private static BigDecimal divide(long xs, int xscale, BigInteger ys, int yscale, long preferredScale, MathContext mc) {
+        // Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+        if (compareMagnitudeNormalized(xs, xscale, ys, yscale) > 0) {// satisfy constraint (b)
+            yscale -= 1; // [that is, divisor *= 10]
+        }
+        int mcp = mc.precision;
+        int roundingMode = mc.roundingMode.oldMode;
+
+        // In order to find out whether the divide generates the exact result,
+        // we avoid calling the above divide method. 'quotient' holds the
+        // return BigDecimal object whose scale will be set to 'scl'.
+        BigDecimal quotient;
+        int scl = checkScaleNonZero(preferredScale + yscale - xscale + mcp);
+        if (checkScaleNonZero((long) mcp + yscale) > xscale) {
+            int raise = checkScaleNonZero((long) mcp + yscale - xscale);
+            BigInteger rb = bigMultiplyPowerTen(xs,raise);
+            quotient = divideAndRound(rb, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+        } else {
+            int newScale = checkScaleNonZero((long) xscale - mcp);
+            int raise = checkScaleNonZero((long) newScale - yscale);
+            BigInteger rb = bigMultiplyPowerTen(ys,raise);
+            quotient = divideAndRound(BigInteger.valueOf(xs), rb, scl, roundingMode,checkScaleNonZero(preferredScale));
+        }
+        // doRound, here, only affects 1000000000 case.
+        return doRound(quotient, mc);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (xs /
+     * ys)}, with rounding according to the context settings.
+     */
+    private static BigDecimal divide(BigInteger xs, int xscale, BigInteger ys, int yscale, long preferredScale, MathContext mc) {
+        // Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+        if (compareMagnitudeNormalized(xs, xscale, ys, yscale) > 0) {// satisfy constraint (b)
+            yscale -= 1; // [that is, divisor *= 10]
+        }
+        int mcp = mc.precision;
+        int roundingMode = mc.roundingMode.oldMode;
+
+        // In order to find out whether the divide generates the exact result,
+        // we avoid calling the above divide method. 'quotient' holds the
+        // return BigDecimal object whose scale will be set to 'scl'.
+        BigDecimal quotient;
+        int scl = checkScaleNonZero(preferredScale + yscale - xscale + mcp);
+        if (checkScaleNonZero((long) mcp + yscale) > xscale) {
+            int raise = checkScaleNonZero((long) mcp + yscale - xscale);
+            BigInteger rb = bigMultiplyPowerTen(xs,raise);
+            quotient = divideAndRound(rb, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+        } else {
+            int newScale = checkScaleNonZero((long) xscale - mcp);
+            int raise = checkScaleNonZero((long) newScale - yscale);
+            BigInteger rb = bigMultiplyPowerTen(ys,raise);
+            quotient = divideAndRound(xs, rb, scl, roundingMode,checkScaleNonZero(preferredScale));
+        }
+        // doRound, here, only affects 1000000000 case.
+        return doRound(quotient, mc);
+    }
+
+    /*
+     * performs divideAndRound for (dividend0*dividend1, divisor)
+     * returns null if quotient can't fit into long value;
+     */
+    private static BigDecimal multiplyDivideAndRound(long dividend0, long dividend1, long divisor, int scale, int roundingMode,
+                                                     int preferredScale) {
+        int qsign = Long.signum(dividend0)*Long.signum(dividend1)*Long.signum(divisor);
+        dividend0 = Math.abs(dividend0);
+        dividend1 = Math.abs(dividend1);
+        divisor = Math.abs(divisor);
+        // multiply dividend0 * dividend1
+        long d0_hi = dividend0 >>> 32;
+        long d0_lo = dividend0 & LONG_MASK;
+        long d1_hi = dividend1 >>> 32;
+        long d1_lo = dividend1 & LONG_MASK;
+        long product = d0_lo * d1_lo;
+        long d0 = product & LONG_MASK;
+        long d1 = product >>> 32;
+        product = d0_hi * d1_lo + d1;
+        d1 = product & LONG_MASK;
+        long d2 = product >>> 32;
+        product = d0_lo * d1_hi + d1;
+        d1 = product & LONG_MASK;
+        d2 += product >>> 32;
+        long d3 = d2>>>32;
+        d2 &= LONG_MASK;
+        product = d0_hi*d1_hi + d2;
+        d2 = product & LONG_MASK;
+        d3 = ((product>>>32) + d3) & LONG_MASK;
+        final long dividendHi = make64(d3,d2);
+        final long dividendLo = make64(d1,d0);
+        // divide
+        return divideAndRound128(dividendHi, dividendLo, divisor, qsign, scale, roundingMode, preferredScale);
+    }
+
+    private static final long DIV_NUM_BASE = (1L<<32); // Number base (32 bits).
+
+    /*
+     * divideAndRound 128-bit value by long divisor.
+     * returns null if quotient can't fit into long value;
+     * Specialized version of Knuth's division
+     */
+    private static BigDecimal divideAndRound128(final long dividendHi, final long dividendLo, long divisor, int sign,
+                                                int scale, int roundingMode, int preferredScale) {
+        if (dividendHi >= divisor) {
+            return null;
+        }
+        final int shift = Long.numberOfLeadingZeros(divisor);
+        divisor <<= shift;
+
+        final long v1 = divisor >>> 32;
+        final long v0 = divisor & LONG_MASK;
+
+        long q1, q0;
+        long r_tmp;
+
+        long tmp = dividendLo << shift;
+        long u1 = tmp >>> 32;
+        long u0 = tmp & LONG_MASK;
+
+        tmp = (dividendHi << shift) | (dividendLo >>> 64 - shift);
+        long u2 = tmp & LONG_MASK;
+        tmp = divWord(tmp,v1);
+        q1 = tmp & LONG_MASK;
+        r_tmp = tmp >>> 32;
+        while(q1 >= DIV_NUM_BASE || unsignedLongCompare(q1*v0, make64(r_tmp, u1))) {
+            q1--;
+            r_tmp += v1;
+            if (r_tmp >= DIV_NUM_BASE)
+                break;
+        }
+        tmp = mulsub(u2,u1,v1,v0,q1);
+        u1 = tmp & LONG_MASK;
+        tmp = divWord(tmp,v1);
+        q0 = tmp & LONG_MASK;
+        r_tmp = tmp >>> 32;
+        while(q0 >= DIV_NUM_BASE || unsignedLongCompare(q0*v0,make64(r_tmp,u0))) {
+            q0--;
+            r_tmp += v1;
+            if (r_tmp >= DIV_NUM_BASE)
+                break;
+        }
+        if((int)q1 < 0) {
+            // result (which is positive and unsigned here)
+            // can't fit into long due to sign bit is used for value
+            MutableBigInteger mq = new MutableBigInteger(new int[]{(int)q1, (int)q0});
+            if (roundingMode == ROUND_DOWN && scale == preferredScale) {
+                return mq.toBigDecimal(sign, scale);
+            }
+            long r = mulsub(u1, u0, v1, v0, q0) >>> shift;
+            if (r != 0) {
+                if(needIncrement(divisor >>> shift, roundingMode, sign, mq, r)){
+                    mq.add(MutableBigInteger.ONE);
+                }
+                return mq.toBigDecimal(sign, scale);
+            } else {
+                if (preferredScale != scale) {
+                    BigInteger intVal =  mq.toBigInteger(sign);
+                    return createAndStripZerosToMatchScale(intVal,scale, preferredScale);
+                } else {
+                    return mq.toBigDecimal(sign, scale);
+                }
+            }
+        }
+        long q = make64(q1,q0);
+        q*=sign;
+        if (roundingMode == ROUND_DOWN && scale == preferredScale)
+            return valueOf(q, scale);
+        long r = mulsub(u1, u0, v1, v0, q0) >>> shift;
+        if (r != 0) {
+            boolean increment = needIncrement(divisor >>> shift, roundingMode, sign, q, r);
+            return valueOf((increment ? q + sign : q), scale);
+        } else {
+            if (preferredScale != scale) {
+                return createAndStripZerosToMatchScale(q, scale, preferredScale);
+            } else {
+                return valueOf(q, scale);
+            }
+        }
+    }
+
+    /*
+     * calculate divideAndRound for ldividend*10^raise / divisor
+     * when abs(dividend)==abs(divisor);
+     */
+    private static BigDecimal roundedTenPower(int qsign, int raise, int scale, int preferredScale) {
+        if (scale > preferredScale) {
+            int diff = scale - preferredScale;
+            if(diff < raise) {
+                return scaledTenPow(raise - diff, qsign, preferredScale);
+            } else {
+                return valueOf(qsign,scale-raise);
+            }
+        } else {
+            return scaledTenPow(raise, qsign, scale);
+        }
+    }
+
+    static BigDecimal scaledTenPow(int n, int sign, int scale) {
+        if (n < LONG_TEN_POWERS_TABLE.length)
+            return valueOf(sign*LONG_TEN_POWERS_TABLE[n],scale);
+        else {
+            BigInteger unscaledVal = bigTenToThe(n);
+            if(sign==-1) {
+                unscaledVal = unscaledVal.negate();
+            }
+            return new BigDecimal(unscaledVal, INFLATED, scale, n+1);
+        }
+    }
+
+    private static long divWord(long n, long dLong) {
+        long r;
+        long q;
+        if (dLong == 1) {
+            q = (int)n;
+            return (q & LONG_MASK);
+        }
+        // Approximate the quotient and remainder
+        q = (n >>> 1) / (dLong >>> 1);
+        r = n - q*dLong;
+
+        // Correct the approximation
+        while (r < 0) {
+            r += dLong;
+            q--;
+        }
+        while (r >= dLong) {
+            r -= dLong;
+            q++;
+        }
+        // n - q*dlong == r && 0 <= r <dLong, hence we're done.
+        return (r << 32) | (q & LONG_MASK);
+    }
+
+    private static long make64(long hi, long lo) {
+        return hi<<32 | lo;
+    }
+
+    private static long mulsub(long u1, long u0, final long v1, final long v0, long q0) {
+        long tmp = u0 - q0*v0;
+        return make64(u1 + (tmp>>>32) - q0*v1,tmp & LONG_MASK);
+    }
+
+    private static boolean unsignedLongCompare(long one, long two) {
+        return (one+Long.MIN_VALUE) > (two+Long.MIN_VALUE);
+    }
+
+    private static boolean unsignedLongCompareEq(long one, long two) {
+        return (one+Long.MIN_VALUE) >= (two+Long.MIN_VALUE);
+    }
+
+
+    // Compare Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+    private static int compareMagnitudeNormalized(long xs, int xscale, long ys, int yscale) {
+        // assert xs!=0 && ys!=0
+        int sdiff = xscale - yscale;
+        if (sdiff != 0) {
+            if (sdiff < 0) {
+                xs = longMultiplyPowerTen(xs, -sdiff);
+            } else { // sdiff > 0
+                ys = longMultiplyPowerTen(ys, sdiff);
+            }
+        }
+        if (xs != INFLATED)
+            return (ys != INFLATED) ? longCompareMagnitude(xs, ys) : -1;
+        else
+            return 1;
+    }
+
+    // Compare Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+    private static int compareMagnitudeNormalized(long xs, int xscale, BigInteger ys, int yscale) {
+        // assert "ys can't be represented as long"
+        if (xs == 0)
+            return -1;
+        int sdiff = xscale - yscale;
+        if (sdiff < 0) {
+            if (longMultiplyPowerTen(xs, -sdiff) == INFLATED ) {
+                return bigMultiplyPowerTen(xs, -sdiff).compareMagnitude(ys);
+            }
+        }
+        return -1;
+    }
+
+    // Compare Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+    private static int compareMagnitudeNormalized(BigInteger xs, int xscale, BigInteger ys, int yscale) {
+        int sdiff = xscale - yscale;
+        if (sdiff < 0) {
+            return bigMultiplyPowerTen(xs, -sdiff).compareMagnitude(ys);
+        } else { // sdiff >= 0
+            return xs.compareMagnitude(bigMultiplyPowerTen(ys, sdiff));
+        }
+    }
+
+    private static long multiply(long x, long y){
+                long product = x * y;
+        long ax = Math.abs(x);
+        long ay = Math.abs(y);
+        if (((ax | ay) >>> 31 == 0) || (y == 0) || (product / y == x)){
+                        return product;
+                }
+        return INFLATED;
+    }
+
+    private static BigDecimal multiply(long x, long y, int scale) {
+        long product = multiply(x, y);
+        if(product!=INFLATED) {
+            return valueOf(product,scale);
+        }
+        return new BigDecimal(BigInteger.valueOf(x).multiply(y),INFLATED,scale,0);
+    }
+
+    private static BigDecimal multiply(long x, BigInteger y, int scale) {
+        if(x==0) {
+            return zeroValueOf(scale);
+        }
+        return new BigDecimal(y.multiply(x),INFLATED,scale,0);
+    }
+
+    private static BigDecimal multiply(BigInteger x, BigInteger y, int scale) {
+        return new BigDecimal(x.multiply(y),INFLATED,scale,0);
+    }
+
+    /**
+     * Multiplies two long values and rounds according {@code MathContext}
+     */
+    private static BigDecimal multiplyAndRound(long x, long y, int scale, MathContext mc) {
+        long product = multiply(x, y);
+        if(product!=INFLATED) {
+            return doRound(product, scale, mc);
+        }
+        // attempt to do it in 128 bits
+        int rsign = 1;
+        if(x < 0) {
+            x = -x;
+            rsign = -1;
+        }
+        if(y < 0) {
+            y = -y;
+            rsign *= -1;
+        }
+        // multiply dividend0 * dividend1
+        long m0_hi = x >>> 32;
+        long m0_lo = x & LONG_MASK;
+        long m1_hi = y >>> 32;
+        long m1_lo = y & LONG_MASK;
+        product = m0_lo * m1_lo;
+        long m0 = product & LONG_MASK;
+        long m1 = product >>> 32;
+        product = m0_hi * m1_lo + m1;
+        m1 = product & LONG_MASK;
+        long m2 = product >>> 32;
+        product = m0_lo * m1_hi + m1;
+        m1 = product & LONG_MASK;
+        m2 += product >>> 32;
+        long m3 = m2>>>32;
+        m2 &= LONG_MASK;
+        product = m0_hi*m1_hi + m2;
+        m2 = product & LONG_MASK;
+        m3 = ((product>>>32) + m3) & LONG_MASK;
+        final long mHi = make64(m3,m2);
+        final long mLo = make64(m1,m0);
+        BigDecimal res = doRound128(mHi, mLo, rsign, scale, mc);
+        if(res!=null) {
+            return res;
+        }
+        res = new BigDecimal(BigInteger.valueOf(x).multiply(y*rsign), INFLATED, scale, 0);
+        return doRound(res,mc);
+    }
+
+    private static BigDecimal multiplyAndRound(long x, BigInteger y, int scale, MathContext mc) {
+        if(x==0) {
+            return zeroValueOf(scale);
+        }
+        return doRound(y.multiply(x), scale, mc);
+    }
+
+    private static BigDecimal multiplyAndRound(BigInteger x, BigInteger y, int scale, MathContext mc) {
+        return doRound(x.multiply(y), scale, mc);
+    }
+
+    /**
+     * rounds 128-bit value according {@code MathContext}
+     * returns null if result can't be repsented as compact BigDecimal.
+     */
+    private static BigDecimal doRound128(long hi, long lo, int sign, int scale, MathContext mc) {
+        int mcp = mc.precision;
+        int drop;
+        BigDecimal res = null;
+        if(((drop = precision(hi, lo) - mcp) > 0)&&(drop<LONG_TEN_POWERS_TABLE.length)) {
+            scale = checkScaleNonZero((long)scale - drop);
+            res = divideAndRound128(hi, lo, LONG_TEN_POWERS_TABLE[drop], sign, scale, mc.roundingMode.oldMode, scale);
+        }
+        if(res!=null) {
+            return doRound(res,mc);
+        }
+        return null;
+    }
+
+    private static final long[][] LONGLONG_TEN_POWERS_TABLE = {
+        {   0L, 0x8AC7230489E80000L },  //10^19
+        {       0x5L, 0x6bc75e2d63100000L },  //10^20
+        {       0x36L, 0x35c9adc5dea00000L },  //10^21
+        {       0x21eL, 0x19e0c9bab2400000L  },  //10^22
+        {       0x152dL, 0x02c7e14af6800000L  },  //10^23
+        {       0xd3c2L, 0x1bcecceda1000000L  },  //10^24
+        {       0x84595L, 0x161401484a000000L  },  //10^25
+        {       0x52b7d2L, 0xdcc80cd2e4000000L  },  //10^26
+        {       0x33b2e3cL, 0x9fd0803ce8000000L  },  //10^27
+        {       0x204fce5eL, 0x3e25026110000000L  },  //10^28
+        {       0x1431e0faeL, 0x6d7217caa0000000L  },  //10^29
+        {       0xc9f2c9cd0L, 0x4674edea40000000L  },  //10^30
+        {       0x7e37be2022L, 0xc0914b2680000000L  },  //10^31
+        {       0x4ee2d6d415bL, 0x85acef8100000000L  },  //10^32
+        {       0x314dc6448d93L, 0x38c15b0a00000000L  },  //10^33
+        {       0x1ed09bead87c0L, 0x378d8e6400000000L  },  //10^34
+        {       0x13426172c74d82L, 0x2b878fe800000000L  },  //10^35
+        {       0xc097ce7bc90715L, 0xb34b9f1000000000L  },  //10^36
+        {       0x785ee10d5da46d9L, 0x00f436a000000000L  },  //10^37
+        {       0x4b3b4ca85a86c47aL, 0x098a224000000000L  },  //10^38
+    };
+
+    /*
+     * returns precision of 128-bit value
+     */
+    private static int precision(long hi, long lo){
+        if(hi==0) {
+            if(lo>=0) {
+                return longDigitLength(lo);
+            }
+            return (unsignedLongCompareEq(lo, LONGLONG_TEN_POWERS_TABLE[0][1])) ? 20 : 19;
+            // 0x8AC7230489E80000L  = unsigned 2^19
+        }
+        int r = ((128 - Long.numberOfLeadingZeros(hi) + 1) * 1233) >>> 12;
+        int idx = r-19;
+        return (idx >= LONGLONG_TEN_POWERS_TABLE.length || longLongCompareMagnitude(hi, lo,
+                                                                                    LONGLONG_TEN_POWERS_TABLE[idx][0], LONGLONG_TEN_POWERS_TABLE[idx][1])) ? r : r + 1;
+    }
+
+    /*
+     * returns true if 128 bit number <hi0,lo0> is less then <hi1,lo1>
+     * hi0 & hi1 should be non-negative
+     */
+    private static boolean longLongCompareMagnitude(long hi0, long lo0, long hi1, long lo1) {
+        if(hi0!=hi1) {
+            return hi0<hi1;
+        }
+        return (lo0+Long.MIN_VALUE) <(lo1+Long.MIN_VALUE);
+    }
+
+    private static BigDecimal divide(long dividend, int dividendScale, long divisor, int divisorScale, int scale, int roundingMode) {
+        if (checkScale(dividend,(long)scale + divisorScale) > dividendScale) {
+            int newScale = scale + divisorScale;
+            int raise = newScale - dividendScale;
+            if(raise<LONG_TEN_POWERS_TABLE.length) {
+                long xs = dividend;
+                if ((xs = longMultiplyPowerTen(xs, raise)) != INFLATED) {
+                    return divideAndRound(xs, divisor, scale, roundingMode, scale);
+                }
+                BigDecimal q = multiplyDivideAndRound(LONG_TEN_POWERS_TABLE[raise], dividend, divisor, scale, roundingMode, scale);
+                if(q!=null) {
+                    return q;
+                }
+            }
+            BigInteger scaledDividend = bigMultiplyPowerTen(dividend, raise);
+            return divideAndRound(scaledDividend, divisor, scale, roundingMode, scale);
+        } else {
+            int newScale = checkScale(divisor,(long)dividendScale - scale);
+            int raise = newScale - divisorScale;
+            if(raise<LONG_TEN_POWERS_TABLE.length) {
+                long ys = divisor;
+                if ((ys = longMultiplyPowerTen(ys, raise)) != INFLATED) {
+                    return divideAndRound(dividend, ys, scale, roundingMode, scale);
+                }
+            }
+            BigInteger scaledDivisor = bigMultiplyPowerTen(divisor, raise);
+            return divideAndRound(BigInteger.valueOf(dividend), scaledDivisor, scale, roundingMode, scale);
+        }
+    }
+
+    private static BigDecimal divide(BigInteger dividend, int dividendScale, long divisor, int divisorScale, int scale, int roundingMode) {
+        if (checkScale(dividend,(long)scale + divisorScale) > dividendScale) {
+            int newScale = scale + divisorScale;
+            int raise = newScale - dividendScale;
+            BigInteger scaledDividend = bigMultiplyPowerTen(dividend, raise);
+            return divideAndRound(scaledDividend, divisor, scale, roundingMode, scale);
+        } else {
+            int newScale = checkScale(divisor,(long)dividendScale - scale);
+            int raise = newScale - divisorScale;
+            if(raise<LONG_TEN_POWERS_TABLE.length) {
+                long ys = divisor;
+                if ((ys = longMultiplyPowerTen(ys, raise)) != INFLATED) {
+                    return divideAndRound(dividend, ys, scale, roundingMode, scale);
+                }
+            }
+            BigInteger scaledDivisor = bigMultiplyPowerTen(divisor, raise);
+            return divideAndRound(dividend, scaledDivisor, scale, roundingMode, scale);
+        }
+    }
+
+    private static BigDecimal divide(long dividend, int dividendScale, BigInteger divisor, int divisorScale, int scale, int roundingMode) {
+        if (checkScale(dividend,(long)scale + divisorScale) > dividendScale) {
+            int newScale = scale + divisorScale;
+            int raise = newScale - dividendScale;
+            BigInteger scaledDividend = bigMultiplyPowerTen(dividend, raise);
+            return divideAndRound(scaledDividend, divisor, scale, roundingMode, scale);
+        } else {
+            int newScale = checkScale(divisor,(long)dividendScale - scale);
+            int raise = newScale - divisorScale;
+            BigInteger scaledDivisor = bigMultiplyPowerTen(divisor, raise);
+            return divideAndRound(BigInteger.valueOf(dividend), scaledDivisor, scale, roundingMode, scale);
+        }
+    }
+
+    private static BigDecimal divide(BigInteger dividend, int dividendScale, BigInteger divisor, int divisorScale, int scale, int roundingMode) {
+        if (checkScale(dividend,(long)scale + divisorScale) > dividendScale) {
+            int newScale = scale + divisorScale;
+            int raise = newScale - dividendScale;
+            BigInteger scaledDividend = bigMultiplyPowerTen(dividend, raise);
+            return divideAndRound(scaledDividend, divisor, scale, roundingMode, scale);
+        } else {
+            int newScale = checkScale(divisor,(long)dividendScale - scale);
+            int raise = newScale - divisorScale;
+            BigInteger scaledDivisor = bigMultiplyPowerTen(divisor, raise);
+            return divideAndRound(dividend, scaledDivisor, scale, roundingMode, scale);
+        }
+    }
+
 }
--- a/src/share/classes/java/math/BigInteger.java	Thu Sep 01 13:53:59 2011 +0100
+++ b/src/share/classes/java/math/BigInteger.java	Thu Sep 01 23:00:09 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -353,27 +353,17 @@
         mag = trustedStripLeadingZeroInts(magnitude);
     }
 
-    // Constructs a new BigInteger using a char array with radix=10
-    BigInteger(char[] val) {
+    /*
+     * Constructs a new BigInteger using a char array with radix=10.
+     * Sign is precalculated outside and not allowed in the val.
+     */
+    BigInteger(char[] val, int sign, int len) {
         int cursor = 0, numDigits;
-        int len = val.length;
-
-        // Check for leading minus sign
-        int sign = 1;
-        if (val[0] == '-') {
-            if (len == 1)
-                throw new NumberFormatException("Zero length BigInteger");
-            sign = -1;
-            cursor = 1;
-        } else if (val[0] == '+') {
-            if (len == 1)
-                throw new NumberFormatException("Zero length BigInteger");
-            cursor = 1;
+
+        // Skip leading zeros and compute number of digits in magnitude
+        while (cursor < len && Character.digit(val[cursor], 10) == 0) {
+            cursor++;
         }
-
-        // Skip leading zeros and compute number of digits in magnitude
-        while (cursor < len && Character.digit(val[cursor], 10) == 0)
-            cursor++;
         if (cursor == len) {
             signum = 0;
             mag = ZERO.mag;
@@ -382,7 +372,6 @@
 
         numDigits = len - cursor;
         signum = sign;
-
         // Pre-allocate array of expected size
         int numWords;
         if (len < 10) {
@@ -1058,6 +1047,73 @@
     }
 
     /**
+     * Package private methods used by BigDecimal code to add a BigInteger
+     * with a long. Assumes val is not equal to INFLATED.
+     */
+    BigInteger add(long val) {
+        if (val == 0)
+            return this;
+        if (signum == 0)
+            return valueOf(val);
+        if (Long.signum(val) == signum)
+            return new BigInteger(add(mag, Math.abs(val)), signum);
+        int cmp = compareMagnitude(val);
+        if (cmp == 0)
+            return ZERO;
+        int[] resultMag = (cmp > 0 ? subtract(mag, Math.abs(val)) : subtract(Math.abs(val), mag));
+        resultMag = trustedStripLeadingZeroInts(resultMag);
+        return new BigInteger(resultMag, cmp == signum ? 1 : -1);
+    }
+
+    /**
+     * Adds the contents of the int array x and long value val. This
+     * method allocates a new int array to hold the answer and returns
+     * a reference to that array.  Assumes x.length &gt; 0 and val is
+     * non-negative
+     */
+    private static int[] add(int[] x, long val) {
+        int[] y;
+        long sum = 0;
+        int xIndex = x.length;
+        int[] result;
+        int highWord = (int)(val >>> 32);
+        if (highWord==0) {
+            result = new int[xIndex];
+            sum = (x[--xIndex] & LONG_MASK) + val;
+            result[xIndex] = (int)sum;
+        } else {
+            if (xIndex == 1) {
+                result = new int[2];
+                sum = val  + (x[0] & LONG_MASK);
+                result[1] = (int)sum;
+                result[0] = (int)(sum >>> 32);
+                return result;
+            } else {
+                result = new int[xIndex];
+                sum = (x[--xIndex] & LONG_MASK) + (val & LONG_MASK);
+                result[xIndex] = (int)sum;
+                sum = (x[--xIndex] & LONG_MASK) + (highWord & LONG_MASK) + (sum >>> 32);
+                result[xIndex] = (int)sum;
+            }
+        }
+        // Copy remainder of longer number while carry propagation is required
+        boolean carry = (sum >>> 32 != 0);
+        while (xIndex > 0 && carry)
+            carry = ((result[--xIndex] = x[xIndex] + 1) == 0);
+        // Copy remainder of longer number
+        while (xIndex > 0)
+            result[--xIndex] = x[xIndex];
+        // Grow result if necessary
+        if (carry) {
+            int bigger[] = new int[result.length + 1];
+            System.arraycopy(result, 0, bigger, 1, result.length);
+            bigger[0] = 0x01;
+            return bigger;
+        }
+        return result;
+    }
+
+    /**
      * Adds the contents of the int arrays x and y. This method allocates
      * a new int array to hold the answer and returns a reference to that
      * array.
@@ -1074,14 +1130,17 @@
         int yIndex = y.length;
         int result[] = new int[xIndex];
         long sum = 0;
-
-        // Add common parts of both numbers
-        while(yIndex > 0) {
-            sum = (x[--xIndex] & LONG_MASK) +
-                  (y[--yIndex] & LONG_MASK) + (sum >>> 32);
+        if(yIndex==1) {
+            sum = (x[--xIndex] & LONG_MASK) + (y[0] & LONG_MASK) ;
             result[xIndex] = (int)sum;
+        } else {
+            // Add common parts of both numbers
+            while(yIndex > 0) {
+                sum = (x[--xIndex] & LONG_MASK) +
+                      (y[--yIndex] & LONG_MASK) + (sum >>> 32);
+                result[xIndex] = (int)sum;
+            }
         }
-
         // Copy remainder of longer number while carry propagation is required
         boolean carry = (sum >>> 32 != 0);
         while (xIndex > 0 && carry)
@@ -1101,6 +1160,71 @@
         return result;
     }
 
+    private static int[] subtract(long val, int[] little) {
+        int highWord = (int)(val >>> 32);
+        if (highWord==0) {
+            int result[] = new int[1];
+            result[0] = (int)(val - (little[0] & LONG_MASK));
+            return result;
+        } else {
+            int result[] = new int[2];
+            if(little.length==1) {
+                long difference = ((int)val & LONG_MASK) - (little[0] & LONG_MASK);
+                result[1] = (int)difference;
+                // Subtract remainder of longer number while borrow propagates
+                boolean borrow = (difference >> 32 != 0);
+                if(borrow) {
+                    result[0] = highWord - 1;
+                } else {        // Copy remainder of longer number
+                    result[0] = highWord;
+                }
+                return result;
+            } else { // little.length==2
+                long difference = ((int)val & LONG_MASK) - (little[1] & LONG_MASK);
+                result[1] = (int)difference;
+                difference = (highWord & LONG_MASK) - (little[0] & LONG_MASK) + (difference >> 32);
+                result[0] = (int)difference;
+                return result;
+            }
+        }
+    }
+
+    /**
+     * Subtracts the contents of the second argument (val) from the
+     * first (big).  The first int array (big) must represent a larger number
+     * than the second.  This method allocates the space necessary to hold the
+     * answer.
+     * assumes val &gt;= 0
+     */
+    private static int[] subtract(int[] big, long val) {
+        int highWord = (int)(val >>> 32);
+        int bigIndex = big.length;
+        int result[] = new int[bigIndex];
+        long difference = 0;
+
+        if (highWord==0) {
+            difference = (big[--bigIndex] & LONG_MASK) - val;
+            result[bigIndex] = (int)difference;
+        } else {
+            difference = (big[--bigIndex] & LONG_MASK) - (val & LONG_MASK);
+            result[bigIndex] = (int)difference;
+            difference = (big[--bigIndex] & LONG_MASK) - (highWord & LONG_MASK) + (difference >> 32);
+            result[bigIndex] = (int)difference;
+        }
+
+
+        // Subtract remainder of longer number while borrow propagates
+        boolean borrow = (difference >> 32 != 0);
+        while (bigIndex > 0 && borrow)
+            borrow = ((result[--bigIndex] = big[bigIndex] - 1) == -1);
+
+        // Copy remainder of longer number
+        while (bigIndex > 0)
+            result[--bigIndex] = big[bigIndex];
+
+        return result;
+    }
+
     /**
      * Returns a BigInteger whose value is {@code (this - val)}.
      *
@@ -1165,11 +1289,39 @@
     public BigInteger multiply(BigInteger val) {
         if (val.signum == 0 || signum == 0)
             return ZERO;
-
+        int resultSign = signum == val.signum ? 1 : -1;
+        if (val.mag.length == 1) {
+            return  multiplyByInt(mag,val.mag[0], resultSign);
+        }
+        if(mag.length == 1) {
+            return multiplyByInt(val.mag,mag[0], resultSign);
+        }
         int[] result = multiplyToLen(mag, mag.length,
                                      val.mag, val.mag.length, null);
         result = trustedStripLeadingZeroInts(result);
-        return new BigInteger(result, signum == val.signum ? 1 : -1);
+        return new BigInteger(result, resultSign);
+    }
+
+    private static BigInteger multiplyByInt(int[] x, int y, int sign) {
+        if(Integer.bitCount(y)==1) {
+            return new BigInteger(shiftLeft(x,Integer.numberOfTrailingZeros(y)), sign);
+        }
+        int xlen = x.length;
+        int[] rmag =  new int[xlen + 1];
+        long carry = 0;
+        long yl = y & LONG_MASK;
+        int rstart = rmag.length - 1;
+        for (int i = xlen - 1; i >= 0; i--) {
+            long product = (x[i] & LONG_MASK) * yl + carry;
+            rmag[rstart--] = (int)product;
+            carry = product >>> 32;
+        }
+        if (carry == 0L) {
+            rmag = java.util.Arrays.copyOfRange(rmag, 1, rmag.length);
+        } else {
+            rmag[rstart] = (int)carry;
+        }
+        return new BigInteger(rmag, sign);
     }
 
     /**
@@ -1339,8 +1491,8 @@
                           a = new MutableBigInteger(this.mag),
                           b = new MutableBigInteger(val.mag);
 
-        a.divide(b, q);
-        return q.toBigInteger(this.signum == val.signum ? 1 : -1);
+        a.divide(b, q, false);
+        return q.toBigInteger(this.signum * val.signum);
     }
 
     /**
@@ -2069,7 +2221,12 @@
                 return shiftRight(-n);
             }
         }
-
+        int[] newMag = shiftLeft(mag,n);
+
+        return new BigInteger(newMag, signum);
+    }
+
+    private static int[] shiftLeft(int[] mag, int n) {
         int nInts = n >>> 5;
         int nBits = n & 0x1f;
         int magLen = mag.length;
@@ -2094,8 +2251,7 @@
                 newMag[i++] = mag[j++] << nBits | mag[j] >>> nBits2;
             newMag[i] = mag[j] << nBits;
         }
-
-        return new BigInteger(newMag, signum);
+        return newMag;
     }
 
     /**
@@ -2530,6 +2686,49 @@
     }
 
     /**
+     * Version of compareMagnitude that compares magnitude with long value.
+     * val can't be Long.MIN_VALUE.
+     */
+    final int compareMagnitude(long val) {
+        assert val != Long.MIN_VALUE;
+        int[] m1 = mag;
+        int len = m1.length;
+        if(len > 2) {
+            return 1;
+        }
+        if (val < 0) {
+            val = -val;
+        }
+        int highWord = (int)(val >>> 32);
+        if (highWord==0) {
+            if (len < 1)
+                return -1;
+            if (len > 1)
+                return 1;
+            int a = m1[0];
+            int b = (int)val;
+            if (a != b) {
+                return ((a & LONG_MASK) < (b & LONG_MASK))? -1 : 1;
+            }
+            return 0;
+        } else {
+            if (len < 2)
+                return -1;
+            int a = m1[0];
+            int b = highWord;
+            if (a != b) {
+                return ((a & LONG_MASK) < (b & LONG_MASK))? -1 : 1;
+            }
+            a = m1[1];
+            b = (int)val;
+            if (a != b) {
+                return ((a & LONG_MASK) < (b & LONG_MASK))? -1 : 1;
+            }
+            return 0;
+        }
+    }
+
+    /**
      * Compares this BigInteger with the specified Object for equality.
      *
      * @param  x Object to which this BigInteger is to be compared.
--- a/src/share/classes/java/math/MutableBigInteger.java	Thu Sep 01 13:53:59 2011 +0100
+++ b/src/share/classes/java/math/MutableBigInteger.java	Thu Sep 01 23:00:09 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -160,7 +160,7 @@
      */
     BigDecimal toBigDecimal(int sign, int scale) {
         if (intLen == 0 || sign == 0)
-            return BigDecimal.valueOf(0, scale);
+            return BigDecimal.zeroValueOf(scale);
         int[] mag = getMagnitudeArray();
         int len = mag.length;
         int d = mag[0];
@@ -171,7 +171,28 @@
         long v = (len == 2) ?
             ((mag[1] & LONG_MASK) | (d & LONG_MASK) << 32) :
             d & LONG_MASK;
-        return new BigDecimal(null, sign == -1 ? -v : v, scale, 0);
+        return BigDecimal.valueOf(sign == -1 ? -v : v, scale);
+    }
+
+    /**
+     * This is for internal use in converting from a MutableBigInteger
+     * object into a long value given a specified sign.
+     * returns INFLATED if value is not fit into long
+     */
+    long toCompactValue(int sign) {
+        if (intLen == 0 || sign == 0)
+            return 0L;
+        int[] mag = getMagnitudeArray();
+        int len = mag.length;
+        int d = mag[0];
+        // If this MutableBigInteger can not be fitted into long, we need to
+        // make a BigInteger object for the resultant BigDecimal object.
+        if (len > 2 || (d < 0 && len == 2))
+            return INFLATED;
+        long v = (len == 2) ?
+            ((mag[1] & LONG_MASK) | (d & LONG_MASK) << 32) :
+            d & LONG_MASK;
+        return sign == -1 ? -v : v;
     }
 
     /**
@@ -544,6 +565,24 @@
     }
 
     /**
+     * The method is the same as mulsun, except the fact that q array is not
+     * updated, the only result of the method is borrow flag.
+     */
+    private int mulsubBorrow(int[] q, int[] a, int x, int len, int offset) {
+        long xLong = x & LONG_MASK;
+        long carry = 0;
+        offset += len;
+        for (int j=len-1; j >= 0; j--) {
+            long product = (a[j] & LONG_MASK) * xLong + carry;
+            long difference = q[offset--] - product;
+            carry = (product >>> 32)
+                     + (((difference & LONG_MASK) >
+                         (((~(int)product) & LONG_MASK))) ? 1:0);
+        }
+        return (int)carry;
+    }
+
+    /**
      * Right shift this MutableBigInteger n bits, where n is
      * less than 32.
      * Assumes that intLen > 0, n > 0 for speed
@@ -842,20 +881,20 @@
             rem = (int) (remLong - (quotient.value[0] * divisorLong));
             remLong = rem & LONG_MASK;
         }
-
         int xlen = intLen;
-        int[] qWord = new int[2];
         while (--xlen > 0) {
-            long dividendEstimate = (remLong<<32) |
-                (value[offset + intLen - xlen] & LONG_MASK);
+            long dividendEstimate = (remLong << 32) |
+                    (value[offset + intLen - xlen] & LONG_MASK);
+            int q;
             if (dividendEstimate >= 0) {
-                qWord[0] = (int) (dividendEstimate / divisorLong);
-                qWord[1] = (int) (dividendEstimate - qWord[0] * divisorLong);
+                q = (int) (dividendEstimate / divisorLong);
+                rem = (int) (dividendEstimate - q * divisorLong);
             } else {
-                divWord(qWord, dividendEstimate, divisor);
+                long tmp = divWord(dividendEstimate, divisor);
+                q = (int) (tmp & LONG_MASK);
+                rem = (int) (tmp >>> 32);
             }
-            quotient.value[intLen - xlen] = qWord[0];
-            rem = qWord[1];
+            quotient.value[intLen - xlen] = q;
             remLong = rem & LONG_MASK;
         }
 
@@ -879,40 +918,45 @@
      *
      */
     MutableBigInteger divide(MutableBigInteger b, MutableBigInteger quotient) {
+        return divide(b,quotient,true);
+    }
+
+    MutableBigInteger divide(MutableBigInteger b, MutableBigInteger quotient, boolean needReminder) {
         if (b.intLen == 0)
             throw new ArithmeticException("BigInteger divide by zero");
 
         // Dividend is zero
         if (intLen == 0) {
             quotient.intLen = quotient.offset;
-            return new MutableBigInteger();
+            return needReminder ? new MutableBigInteger() : null;
         }
 
         int cmp = compare(b);
         // Dividend less than divisor
         if (cmp < 0) {
             quotient.intLen = quotient.offset = 0;
-            return new MutableBigInteger(this);
+            return needReminder ? new MutableBigInteger(this) : null;
         }
         // Dividend equal to divisor
         if (cmp == 0) {
             quotient.value[0] = quotient.intLen = 1;
             quotient.offset = 0;
-            return new MutableBigInteger();
+            return needReminder ? new MutableBigInteger() : null;
         }
 
         quotient.clear();
         // Special case one word divisor
         if (b.intLen == 1) {
             int r = divideOneWord(b.value[b.offset], quotient);
-            if (r == 0)
-                return new MutableBigInteger();
-            return new MutableBigInteger(r);
+            if(needReminder) {
+                if (r == 0)
+                    return new MutableBigInteger();
+                return new MutableBigInteger(r);
+            } else {
+                return null;
+            }
         }
-
-        // Copy divisor value to protect divisor
-        int[] div = Arrays.copyOfRange(b.value, b.offset, b.offset + b.intLen);
-        return divideMagnitude(div, quotient);
+        return divideMagnitude(b, quotient, needReminder);
     }
 
     /**
@@ -940,30 +984,72 @@
         if (d == 0)
             return divideOneWord((int)v, quotient) & LONG_MASK;
         else {
-            int[] div = new int[]{ d, (int)(v & LONG_MASK) };
-            return divideMagnitude(div, quotient).toLong();
+            return divideLongMagnitude(v, quotient).toLong();
         }
     }
 
+    private static void copyAndShift(int[] src, int srcFrom, int srcLen, int[] dst, int dstFrom, int shift) {
+        int n2 = 32 - shift;
+        int c=src[srcFrom];
+        for (int i=0; i < srcLen-1; i++) {
+            int b = c;
+            c = src[++srcFrom];
+            dst[dstFrom+i] = (b << shift) | (c >>> n2);
+        }
+        dst[dstFrom+srcLen-1] = c << shift;
+    }
+
     /**
-     * Divide this MutableBigInteger by the divisor represented by its magnitude
-     * array. The quotient will be placed into the provided quotient object &
+     * Divide this MutableBigInteger by the divisor.
+     * The quotient will be placed into the provided quotient object &
      * the remainder object is returned.
      */
-    private MutableBigInteger divideMagnitude(int[] divisor,
-                                              MutableBigInteger quotient) {
-
-        // Remainder starts as dividend with space for a leading zero
-        MutableBigInteger rem = new MutableBigInteger(new int[intLen + 1]);
-        System.arraycopy(value, offset, rem.value, 1, intLen);
-        rem.intLen = intLen;
-        rem.offset = 1;
+    private MutableBigInteger divideMagnitude(MutableBigInteger div,
+                                              MutableBigInteger quotient,
+                                              boolean needReminder ) {
+        // assert div.intLen > 1
+        // D1 normalize the divisor
+        int shift = Integer.numberOfLeadingZeros(div.value[div.offset]);
+        // Copy divisor value to protect divisor
+        final int dlen = div.intLen;
+        int[] divisor;
+        MutableBigInteger rem; // Remainder starts as dividend with space for a leading zero
+        if (shift > 0) {
+            divisor = new int[dlen];
+            copyAndShift(div.value,div.offset,dlen,divisor,0,shift);
+            if(Integer.numberOfLeadingZeros(value[offset])>=shift) {
+                int[] remarr = new int[intLen + 1];
+                rem = new MutableBigInteger(remarr);
+                rem.intLen = intLen;
+                rem.offset = 1;
+                copyAndShift(value,offset,intLen,remarr,1,shift);
+            } else {
+                int[] remarr = new int[intLen + 2];
+                rem = new MutableBigInteger(remarr);
+                rem.intLen = intLen+1;
+                rem.offset = 1;
+                int rFrom = offset;
+                int c=0;
+                int n2 = 32 - shift;
+                for (int i=1; i < intLen+1; i++,rFrom++) {
+                    int b = c;
+                    c = value[rFrom];
+                    remarr[i] = (b << shift) | (c >>> n2);
+                }
+                remarr[intLen+1] = c << shift;
+            }
+        } else {
+            divisor = Arrays.copyOfRange(div.value, div.offset, div.offset + div.intLen);
+            rem = new MutableBigInteger(new int[intLen + 1]);
+            System.arraycopy(value, offset, rem.value, 1, intLen);
+            rem.intLen = intLen;
+            rem.offset = 1;
+        }
 
         int nlen = rem.intLen;
 
         // Set the quotient size
-        int dlen = divisor.length;
-        int limit = nlen - dlen + 1;
+        final int limit = nlen - dlen + 1;
         if (quotient.value.length < limit) {
             quotient.value = new int[limit];
             quotient.offset = 0;
@@ -971,14 +1057,6 @@
         quotient.intLen = limit;
         int[] q = quotient.value;
 
-        // D1 normalize the divisor
-        int shift = Integer.numberOfLeadingZeros(divisor[0]);
-        if (shift > 0) {
-            // First shift will not grow array
-            BigInteger.primitiveLeftShift(divisor, dlen, shift);
-            // But this one might
-            rem.leftShift(shift);
-        }
 
         // Must insert leading 0 in rem if its length did not change
         if (rem.intLen == nlen) {
@@ -990,10 +1068,9 @@
         int dh = divisor[0];
         long dhLong = dh & LONG_MASK;
         int dl = divisor[1];
-        int[] qWord = new int[2];
 
         // D2 Initialize j
-        for(int j=0; j<limit; j++) {
+        for(int j=0; j<limit-1; j++) {
             // D3 Calculate qhat
             // estimate qhat
             int qhat = 0;
@@ -1013,9 +1090,9 @@
                     qhat = (int) (nChunk / dhLong);
                     qrem = (int) (nChunk - (qhat * dhLong));
                 } else {
-                    divWord(qWord, nChunk, dh);
-                    qhat = qWord[0];
-                    qrem = qWord[1];
+                    long tmp = divWord(nChunk, dh);
+                    qhat = (int) (tmp & LONG_MASK);
+                    qrem = (int) (tmp >>> 32);
                 }
             }
 
@@ -1053,6 +1130,181 @@
             // Store the quotient digit
             q[j] = qhat;
         } // D7 loop on j
+        // D3 Calculate qhat
+        // estimate qhat
+        int qhat = 0;
+        int qrem = 0;
+        boolean skipCorrection = false;
+        int nh = rem.value[limit - 1 + rem.offset];
+        int nh2 = nh + 0x80000000;
+        int nm = rem.value[limit + rem.offset];
+
+        if (nh == dh) {
+            qhat = ~0;
+            qrem = nh + nm;
+            skipCorrection = qrem + 0x80000000 < nh2;
+        } else {
+            long nChunk = (((long) nh) << 32) | (nm & LONG_MASK);
+            if (nChunk >= 0) {
+                qhat = (int) (nChunk / dhLong);
+                qrem = (int) (nChunk - (qhat * dhLong));
+            } else {
+                long tmp = divWord(nChunk, dh);
+                qhat = (int) (tmp & LONG_MASK);
+                qrem = (int) (tmp >>> 32);
+            }
+        }
+        if (qhat != 0) {
+            if (!skipCorrection) { // Correct qhat
+                long nl = rem.value[limit + 1 + rem.offset] & LONG_MASK;
+                long rs = ((qrem & LONG_MASK) << 32) | nl;
+                long estProduct = (dl & LONG_MASK) * (qhat & LONG_MASK);
+
+                if (unsignedLongCompare(estProduct, rs)) {
+                    qhat--;
+                    qrem = (int) ((qrem & LONG_MASK) + dhLong);
+                    if ((qrem & LONG_MASK) >= dhLong) {
+                        estProduct -= (dl & LONG_MASK);
+                        rs = ((qrem & LONG_MASK) << 32) | nl;
+                        if (unsignedLongCompare(estProduct, rs))
+                            qhat--;
+                    }
+                }
+            }
+
+
+            // D4 Multiply and subtract
+            int borrow;
+            rem.value[limit - 1 + rem.offset] = 0;
+            if(needReminder)
+                borrow = mulsub(rem.value, divisor, qhat, dlen, limit - 1 + rem.offset);
+            else
+                borrow = mulsubBorrow(rem.value, divisor, qhat, dlen, limit - 1 + rem.offset);
+
+            // D5 Test remainder
+            if (borrow + 0x80000000 > nh2) {
+                // D6 Add back
+                if(needReminder)
+                    divadd(divisor, rem.value, limit - 1 + 1 + rem.offset);
+                qhat--;
+            }
+
+            // Store the quotient digit
+            q[(limit - 1)] = qhat;
+        }
+
+
+        if(needReminder) {
+            // D8 Unnormalize
+            if (shift > 0)
+                rem.rightShift(shift);
+            rem.normalize();
+        }
+        quotient.normalize();
+        return needReminder ? rem : null;
+    }
+
+    /**
+     * Divide this MutableBigInteger by the divisor represented by positive long
+     * value. The quotient will be placed into the provided quotient object &
+     * the remainder object is returned.
+     */
+    private MutableBigInteger divideLongMagnitude(long ldivisor, MutableBigInteger quotient) {
+        // Remainder starts as dividend with space for a leading zero
+        MutableBigInteger rem = new MutableBigInteger(new int[intLen + 1]);
+        System.arraycopy(value, offset, rem.value, 1, intLen);
+        rem.intLen = intLen;
+        rem.offset = 1;
+
+        int nlen = rem.intLen;
+
+        int limit = nlen - 2 + 1;
+        if (quotient.value.length < limit) {
+            quotient.value = new int[limit];
+            quotient.offset = 0;
+        }
+        quotient.intLen = limit;
+        int[] q = quotient.value;
+
+        // D1 normalize the divisor
+        int shift = Long.numberOfLeadingZeros(ldivisor);
+        if (shift > 0) {
+            ldivisor<<=shift;
+            rem.leftShift(shift);
+        }
+
+        // Must insert leading 0 in rem if its length did not change
+        if (rem.intLen == nlen) {
+            rem.offset = 0;
+            rem.value[0] = 0;
+            rem.intLen++;
+        }
+
+        int dh = (int)(ldivisor >>> 32);
+        long dhLong = dh & LONG_MASK;
+        int dl = (int)(ldivisor & LONG_MASK);
+
+        // D2 Initialize j
+        for (int j = 0; j < limit; j++) {
+            // D3 Calculate qhat
+            // estimate qhat
+            int qhat = 0;
+            int qrem = 0;
+            boolean skipCorrection = false;
+            int nh = rem.value[j + rem.offset];
+            int nh2 = nh + 0x80000000;
+            int nm = rem.value[j + 1 + rem.offset];
+
+            if (nh == dh) {
+                qhat = ~0;
+                qrem = nh + nm;
+                skipCorrection = qrem + 0x80000000 < nh2;
+            } else {
+                long nChunk = (((long) nh) << 32) | (nm & LONG_MASK);
+                if (nChunk >= 0) {
+                    qhat = (int) (nChunk / dhLong);
+                    qrem = (int) (nChunk - (qhat * dhLong));
+                } else {
+                    long tmp = divWord(nChunk, dh);
+                    qhat =(int)(tmp & LONG_MASK);
+                    qrem = (int)(tmp>>>32);
+                }
+            }
+
+            if (qhat == 0)
+                continue;
+
+            if (!skipCorrection) { // Correct qhat
+                long nl = rem.value[j + 2 + rem.offset] & LONG_MASK;
+                long rs = ((qrem & LONG_MASK) << 32) | nl;
+                long estProduct = (dl & LONG_MASK) * (qhat & LONG_MASK);
+
+                if (unsignedLongCompare(estProduct, rs)) {
+                    qhat--;
+                    qrem = (int) ((qrem & LONG_MASK) + dhLong);
+                    if ((qrem & LONG_MASK) >= dhLong) {
+                        estProduct -= (dl & LONG_MASK);
+                        rs = ((qrem & LONG_MASK) << 32) | nl;
+                        if (unsignedLongCompare(estProduct, rs))
+                            qhat--;
+                    }
+                }
+            }
+
+            // D4 Multiply and subtract
+            rem.value[j + rem.offset] = 0;
+            int borrow = mulsubLong(rem.value, dh, dl, qhat,  j + rem.offset);
+
+            // D5 Test remainder
+            if (borrow + 0x80000000 > nh2) {
+                // D6 Add back
+                divaddLong(dh,dl, rem.value, j + 1 + rem.offset);
+                qhat--;
+            }
+
+            // Store the quotient digit
+            q[j] = qhat;
+        } // D7 loop on j
 
         // D8 Unnormalize
         if (shift > 0)
@@ -1064,6 +1316,46 @@
     }
 
     /**
+     * A primitive used for division by long.
+     * Specialized version of the method divadd.
+     * dh is a high part of the divisor, dl is a low part
+     */
+    private int divaddLong(int dh, int dl, int[] result, int offset) {
+        long carry = 0;
+
+        long sum = (dl & LONG_MASK) + (result[1+offset] & LONG_MASK);
+        result[1+offset] = (int)sum;
+
+        sum = (dh & LONG_MASK) + (result[offset] & LONG_MASK) + carry;
+        result[offset] = (int)sum;
+        carry = sum >>> 32;
+        return (int)carry;
+    }
+
+    /**
+     * This method is used for division by long.
+     * Specialized version of the method sulsub.
+     * dh is a high part of the divisor, dl is a low part
+     */
+    private int mulsubLong(int[] q, int dh, int dl, int x, int offset) {
+        long xLong = x & LONG_MASK;
+        offset += 2;
+        long product = (dl & LONG_MASK) * xLong;
+        long difference = q[offset] - product;
+        q[offset--] = (int)difference;
+        long carry = (product >>> 32)
+                 + (((difference & LONG_MASK) >
+                     (((~(int)product) & LONG_MASK))) ? 1:0);
+        product = (dh & LONG_MASK) * xLong + carry;
+        difference = q[offset] - product;
+        q[offset--] = (int)difference;
+        carry = (product >>> 32)
+                 + (((difference & LONG_MASK) >
+                     (((~(int)product) & LONG_MASK))) ? 1:0);
+        return (int)carry;
+    }
+
+    /**
      * Compare two longs as if they were unsigned.
      * Returns true iff one is bigger than two.
      */
@@ -1075,19 +1367,22 @@
      * This method divides a long quantity by an int to estimate
      * qhat for two multi precision numbers. It is used when
      * the signed value of n is less than zero.
+     * Returns long value where high 32 bits contain reminder value and
+     * low 32 bits contain quotient value.
      */
-    private void divWord(int[] result, long n, int d) {
+    static long divWord(long n, int d) {
         long dLong = d & LONG_MASK;
-
+        long r;
+        long q;
         if (dLong == 1) {
-            result[0] = (int)n;
-            result[1] = 0;
-            return;
+            q = (int)n;
+            r = 0;
+            return (r << 32) | (q & LONG_MASK);
         }
 
         // Approximate the quotient and remainder
-        long q = (n >>> 1) / (dLong >>> 1);
-        long r = n - q*dLong;
+        q = (n >>> 1) / (dLong >>> 1);
+        r = n - q*dLong;
 
         // Correct the approximation
         while (r < 0) {
@@ -1098,10 +1393,8 @@
             r -= dLong;
             q++;
         }
-
         // n - q*dlong == r && 0 <= r <dLong, hence we're done.
-        result[0] = (int)q;
-        result[1] = (int)r;
+        return (r << 32) | (q & LONG_MASK);
     }
 
     /**
@@ -1473,5 +1766,4 @@
         mod.subtract(t1);
         return mod;
     }
-
 }
--- a/test/java/math/BigDecimal/DivideMcTests.java	Thu Sep 01 13:53:59 2011 +0100
+++ b/test/java/math/BigDecimal/DivideMcTests.java	Thu Sep 01 23:00:09 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/test/java/math/BigDecimal/FloatDoubleValueTests.java	Thu Sep 01 13:53:59 2011 +0100
+++ b/test/java/math/BigDecimal/FloatDoubleValueTests.java	Thu Sep 01 23:00:09 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2011 Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 6274390
+ * @bug 6274390 7082971
  * @summary Verify {float, double}Value methods work with condensed representation
  * @run main FloatDoubleValueTests
  * @run main/othervm -XX:+AggressiveOpts FloatDoubleValueTests
@@ -79,6 +79,7 @@
     // and double.
     static void testFloatDoubleValue() {
         long longValues[] = {
+            Long.MIN_VALUE, // -2^63
             0,
             1,
             2,
--- a/test/java/math/BigDecimal/RangeTests.java	Thu Sep 01 13:53:59 2011 +0100
+++ b/test/java/math/BigDecimal/RangeTests.java	Thu Sep 01 23:00:09 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/test/java/math/BigDecimal/StrippingZerosTest.java	Thu Sep 01 13:53:59 2011 +0100
+++ b/test/java/math/BigDecimal/StrippingZerosTest.java	Thu Sep 01 23:00:09 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011 Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/test/java/math/BigDecimal/ToPlainStringTests.java	Thu Sep 01 13:53:59 2011 +0100
+++ b/test/java/math/BigDecimal/ToPlainStringTests.java	Thu Sep 01 23:00:09 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it