src/share/classes/java/text/DecimalFormat.java

Print this page

        

@@ -43,10 +43,11 @@
 import java.io.ObjectInputStream;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.math.RoundingMode;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Currency;
 import java.util.Locale;
 import java.util.ResourceBundle;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;

@@ -517,13 +518,27 @@
      * @return The formatted number string
      * @see java.text.FieldPosition
      */
     public StringBuffer format(double number, StringBuffer result,
                                FieldPosition fieldPosition) {
+        // If fieldPosition is a DontCareFieldPosition instance we can try to go to fast-path code.
+        boolean tryFastPath = false;
+        if (fieldPosition == DontCareFieldPosition.INSTANCE) tryFastPath = true;
+        else {
         fieldPosition.setBeginIndex(0);
         fieldPosition.setEndIndex(0);
+        }
 
+        if (tryFastPath) {
+            String tempResult = fastFormat(number);
+            if (tempResult != null) {
+                result.append(tempResult);
+                return result;
+            }
+        }
+
+        // if fast path could not work, we fallback to standard code.
         return format(number, result, fieldPosition.getFieldDelegate());
     }
 
     /**
      * Formats a double to produce a string.

@@ -855,10 +870,596 @@
                 "Cannot format given Object as a Number");
         }
         return delegate.getIterator(sb.toString());
     }
 
+    // Fast path formating algorithm for double ================================
+
+    // Fast-path formatting will be used for format(double) if a number of
+    // conditions are met (see checkAndeStFastPathStatus()):
+    // - Only if instance properties meet the right predefined conditions
+    // - The abs value of the double to format is <= Integer.MAX_VALUE
+
+    // ------ The fast path for double methods -------
+
+    // Decides if rounding up must occur for the passed double.
+    private boolean toBeHalfEvenRounded(double number,
+                                        int integralPartAsInt, int nbIntegerDigits,
+                                        int fractionalPartAsInt, int nbFractionalDigits) {
+        // Index of undecidable rounding interval depending on magnitude of number.
+        int boundIndex = 0;
+        if (integralPartAsInt != 0)
+            boundIndex = maximumFractionDigits + nbIntegerDigits;
+        else if (fractionalPartAsInt != 0) boundIndex = nbFractionalDigits;
+
+        // Check poisition of number relative to "Undecidable Rounding Interval".
+        if (remainingFractionalPart <= safeMinusRoundingBounds[boundIndex])
+            return false; // number is outside below.
+        else if (remainingFractionalPart >= safeMajorRoundingBounds[boundIndex])
+            return true;  // number is outside above.
+        else {
+            // number is inside. We compare against nearest perfect-half.
+            double perfectHalf;
+            if (isCurrencyFormat)
+                 perfectHalf = (((double) integralPartAsInt * 1000.0d) +
+                                (double) (fractionalPartAsInt * 10 + 5)) / 1000.0d;
+            else
+                 perfectHalf = (((double) integralPartAsInt * 10000.0d) +
+                                (double) (fractionalPartAsInt * 10 + 5)) / 10000.0d;
+
+            // Applies healf-even rounding rule.
+            if (number == perfectHalf)
+                 return (((fractionalPartAsInt % 10) & 1) != 0);
+            else return (number > perfectHalf);
+        }
+    }
+
+    // Returns the exact number of digits (radix 10) representing
+    //  the passed paramater i. Sets utility formating fields used in the algorithm
+    private int intStringSize(int i) {
+        if (i <= 99999) {
+            if (i <= 999) {
+                if (i <= 99) {
+                    if (i <= 9) {
+                        if (i == 9) isAllNines = true;
+                        else isAllNines = false;
+                        nbGroupsNeeded = 0;
+                        return 1;
+                    }
+                    else {
+                        if (i == 99) isAllNines = true;
+                        else isAllNines = false;
+                        nbGroupsNeeded = 0;
+                        return 2;
+                    }
+                }
+                else {
+                     if (i == 999) isAllNines = true;
+                     else isAllNines = false;
+                     nbGroupsNeeded = 0;
+                     return 3;
+                }
+            }
+            else if (i <= 9999) {
+                if (i == 9999) isAllNines = true;
+                else isAllNines = false;
+                nbGroupsNeeded = 1;
+                return 4;
+            }
+            else {
+                if (i == 99999) isAllNines = true;
+                else isAllNines = false;
+                nbGroupsNeeded = 1;
+                return 5;
+            }
+        }
+        else {
+            if (i <= 99999999) {
+                if (i <= 999999) {
+                    if (i == 999999) isAllNines = true;
+                    else isAllNines = false;
+                    nbGroupsNeeded = 1;
+                    return 6;
+                }
+                else if (i <= 9999999) {
+                    if (i == 9999999) isAllNines = true;
+                    else isAllNines = false;
+                    nbGroupsNeeded = 2;
+                    return 7;
+                }
+                else {
+                    if (i == 99999999) isAllNines = true;
+                    else isAllNines = false;
+                    nbGroupsNeeded = 2;
+                    return 8;
+                }
+            }
+            else if (i <= 999999999) {
+                if (i == 999999999) isAllNines = true;
+                else isAllNines = false;
+                nbGroupsNeeded = 2;
+                return 9;
+            }
+            else {
+                isAllNines = false;
+                nbGroupsNeeded = 3;
+                return 10;
+            }
+        }
+    }
+
+    // Returns the exact number of digits (radix 10) representing the passed i,
+    // Sets utility formating fields used in the algorithm
+    private int fracStringSize(int i) {
+        if (i > 99) {
+            if (i == 999) isAllNines = true;
+            return 3;
+        }
+        else if (i < 10) {
+            if (i == 9) isAllNines = true;
+            return 1;
+        }
+        else {
+            if (i == 99) isAllNines = true;
+            return 2;
+        }
+    }
+
+
+    // Extracts and returns the 2 (Currency pattern) or 3 (Decimal pattern)
+    //  digits int representing the fractional part of passed double.
+    private int getFracIntegral(double fractional, int nbFractionalDigits) {
+        int fractAsInt;
+        double nextFractional;
+        if (nbFractionalDigits == 2) {
+            // Default currency pattern case. 2 fractional digits. Scale 100.0d.
+            nextFractional = fractional * 100.0d;
+            fractAsInt = (int) nextFractional;
+        }
+        else {
+            // Default decimal pattern case. 3 fractional digits. Scale 1000.0d
+            nextFractional = fractional * 1000.0d;
+            fractAsInt = (int) nextFractional;
+        }
+
+        // The remaining fractional part that will be used in rounding decision ...
+        remainingFractionalPart = nextFractional - (double) fractAsInt;
+
+        return fractAsInt;
+    }
+
+
+    // Check validity of using fast path for this instance.
+    // If fast-path is valid for this instance, sets fast-path state as true
+    // and initialize fast-path utility fields as needed.
+    //
+    // FAST-PATH RULES:
+    //   Similar to the default DecimalFormat instantiation case. More precisely:
+    //   - HALF_EVEN rounding mode,
+    //   - isGroupingUsed() is true,
+    //   - groupingSize of 3,
+    //   - multiplier is 1,
+    //   - Decimal separator not mandatory,
+    //   - No use of exponential notation,
+    //   - minimumIntegerDigits is exactly 1 and maximumIntegerDigits at least 10
+    //   - for number of fractional digits, the exact values found in the default case:
+    //       Currency : min = max = 2.
+    //       Decimal  : min = 0. max = 3.
+    private void checkAndSetFastPathStatus() {
+        boolean fastPathWasOn = isFastPath;
+        if ((roundingMode == RoundingMode.HALF_EVEN) &&
+            (isGroupingUsed()) &&
+            (groupingSize == 3) &&
+            (multiplier == 1) &&
+            (!decimalSeparatorAlwaysShown) &&
+            (!useExponentialNotation)) {
+
+            // The fast-path algorithm is semi-hardcoded against
+            //  minimumIntegerDigits and maximumIntegerDigits.
+            if ((minimumIntegerDigits == 1) &&
+                (maximumIntegerDigits >= 10)) isFastPath = true;
+            else isFastPath = false;
+
+            // The fast-path algorithm is hardcoded against
+            //  minimumFractionDigits and maximumFractionDigits.
+            if (isFastPath) {
+                if (isCurrencyFormat) {
+                    if ((minimumFractionDigits != 2) ||
+                        (maximumFractionDigits != 2)) isFastPath = false;
+                }
+                else if ((minimumFractionDigits != 0) ||
+                         (maximumFractionDigits != 3)) isFastPath = false;
+            }
+        }
+        else isFastPath = false;
+
+        if (isFastPath) {
+            // Sets up the locale specific values used when formatting.
+            zeroDelta = symbols.getZeroDigit() - '0'; // '0' is our default representation of zero.
+            groupingChar = symbols.getGroupingSeparator();
+            decimalSeparatorChar = isCurrencyFormat ?
+                symbols.getMonetaryDecimalSeparator() :
+                symbols.getDecimalSeparator();
+
+            // Instantiate and initialize char container arrays used in formatting.
+            int maxNbIntegralDigits = 10;
+            int maxNbGroups = 3;
+
+            // Max size for positive numbers.
+            int maxContainerSize =
+                positivePrefix.length() +
+                maxNbIntegralDigits +
+                maxNbGroups +
+                1 + maximumFractionDigits +
+                positiveSuffix.length();
+
+            char[] template = new char[maxContainerSize];
+            positivePrefix.getChars(0, positivePrefix.length(), template, 0);
+            positiveFastPathContainer = template;
+
+            // Max size for negative numbers.
+            maxContainerSize =
+                negativePrefix.length() +
+                maxNbIntegralDigits +
+                maxNbGroups +
+                1 + maximumFractionDigits +
+                negativeSuffix.length();
+
+            template = new char[maxContainerSize];
+            negativePrefix.getChars(0, negativePrefix.length(), template, 0);
+            negativeFastPathContainer = template;
+
+            // Initialize suffix char arrays.
+            charsPositiveSuffix = positiveSuffix.toCharArray();
+            charsNegativeSuffix = negativeSuffix.toCharArray();
+            isSuffixPresent =
+                (charsPositiveSuffix.length != 0) || (charsNegativeSuffix.length != 0);
+
+            // Initialize rounding interval constants.
+            if (isCurrencyFormat) {
+                safeMinusRoundingBounds = CurrencySafeMinusRoundingBounds;
+                safeMajorRoundingBounds = CurrencySafeMajorRoundingBounds;
+            }
+            else {
+                safeMinusRoundingBounds = StdSafeMinusRoundingBounds;
+                safeMajorRoundingBounds = StdSafeMajorRoundingBounds;
+            }
+        }
+        else if (fastPathWasOn) {
+            // Previous state was fast-path and is no more. Resets cached constants.
+            positiveFastPathContainer = null;
+            negativeFastPathContainer = null;
+            charsPositiveSuffix = null;
+            charsNegativeSuffix = null;
+            safeMajorRoundingBounds = null;
+            safeMinusRoundingBounds = null;
+        }
+
+        fastPathCheckNeeded = false;
+    }
+
+    // Fills efficiently the passed digitsBuffer with digit for up to 4 chars
+    //  for length, starting at startIndex. len must be positive and at most 4.
+    private void fillUpTo4Chars(char[] digitsBuffer, int startIndex,
+                                int len, char digit) {
+        int lowerIndex = startIndex;
+        if (len == 1) digitsBuffer[lowerIndex] = digit;
+        else {
+            int upperIndex = lowerIndex + len - 1; // included !
+            digitsBuffer[upperIndex] = digit;
+            digitsBuffer[lowerIndex] = digit;
+
+            if (len > 2) {
+                // Fill the remaining 1 or 2 digits.
+                digitsBuffer[++lowerIndex] = digit;  // at least one additional digit to fill
+                if (--upperIndex > lowerIndex) digitsBuffer[upperIndex] = digit;
+            }
+        }
+    }
+
+    // Appends positive or negative suffix, depending on sign. Updates lastUsedIndex.
+    private void appendSuffix(boolean isNegative, char[] internalChars) {
+        // Init to factorise on suffix code
+        char[] suffix;
+        if (isNegative)
+             suffix = charsNegativeSuffix;
+        else suffix = charsPositiveSuffix;
+        int len = suffix.length;
+
+        // Trivial and most frequent case.
+        if (len == 0) return;
+
+        // We need to append something.
+        int startIndex = lastUsedIndex + 1;
+        if (len == 1) internalChars[startIndex] = suffix[0];
+        else if (len <= 4) {
+            int dstLower = startIndex;
+            int dstUpper = dstLower + len - 1;
+            int srcUpper = len - 1;
+            internalChars[dstLower++] = suffix[0];
+            internalChars[dstUpper--] = suffix[srcUpper];
+
+            if (len > 2) internalChars[dstLower] = suffix[1];
+            if (len == 4) internalChars[dstUpper] = suffix[2];
+        }
+        else System.arraycopy(suffix, 0, internalChars, startIndex, len);
+
+        lastUsedIndex += len;
+    }
+
+    // Converts digit chars to current locale.
+    // Loops downward starting from LastUsedIndex.
+    private void localizeDigits(char[] digitsBuffer, int startIndex) {
+        // We localize digits only, taking into account currency/decimal fractional case
+        int nonDigitCounter = maximumFractionDigits;
+        for (int cursor = lastUsedIndex; cursor >= startIndex; cursor--) {
+            if (nonDigitCounter != 0) {
+                // Cursor char is a digit char and we must localize it.
+                digitsBuffer[cursor] += zeroDelta;
+                nonDigitCounter--;
+            }
+            else {
+                // Cursor char is decimal separator or grouping char. Just reinit counter.
+                nonDigitCounter = groupingSize;
+            }
+        }
+    }
+
+    // Faster fast-path for the "all nines" cases. Mainly like a "fill" operation.
+    private void formatAllNinesCase(char[] digitsBuffer, int startIndex,
+                                    boolean toBeRounded, int nbGroups,
+                                    int integralLength, int fractionalLength) {
+        char fillDigit;
+        if (!toBeRounded) fillDigit = '9';
+        else {
+            // checks for needed additional 3 digits group.
+            nbGroups += ((integralLength % 3) == 0) ? 1 : 0;
+
+            digitsBuffer[startIndex++] = '1';
+            fillDigit = '0';
+        }
+
+        // We will fill buffer with relevant digit.
+        int totalLength = integralLength + fractionalLength + nbGroups + 1;
+        Arrays.fill(digitsBuffer, startIndex, (startIndex + totalLength), fillDigit);
+
+        // Sets decimal point char and group markers.
+        lastUsedIndex = startIndex + nbGroups + integralLength;
+        int cursorIndex = lastUsedIndex;
+        digitsBuffer[cursorIndex--] = decimalSeparatorChar;
+        while (cursorIndex > startIndex) {
+            if (nbGroups > 0) {
+                cursorIndex -= 3;
+                digitsBuffer[cursorIndex--] = groupingChar;
+                nbGroups--;
+            }
+            else break;
+        }
+
+        // Removes fractional digits if needed.
+        if (!toBeRounded || isCurrencyFormat) lastUsedIndex += fractionalLength;
+        else lastUsedIndex--; // fractional part absent
+    }
+
+    // Collects integral digits from passed number, setting group chars if needed.
+    // No rounding done here.
+    // Loops downward starting at backwardIndex position (inclusive).
+    private void collectIntegralDigits(int number,
+                                       char[] digitsBuffer, int backwardIndex) {
+        int index = backwardIndex;
+        int q;
+        int r;
+        while (number > 999) {
+            // Generate 3 digits per iteration.
+            q = number / 1000;
+            r = number - (q << 10) + (q << 4) + (q << 3); // -1024 +16 +8 = 1000.
+            number = q;
+
+            digitsBuffer[index--] = DigitOnes1000[r];
+            digitsBuffer[index--] = DigitTens1000[r];
+            digitsBuffer[index--] = DigitHundreds1000[r];
+            digitsBuffer[index--] = groupingChar;
+        }
+
+        // Collects last 3 or less digits.
+        digitsBuffer[index--] = DigitOnes1000[number];
+        if (number > 9) {
+            if (number > 99) {
+                digitsBuffer[index--] = DigitTens1000[number];
+                digitsBuffer[index]   = DigitHundreds1000[number];
+            }
+            else digitsBuffer[index]  = DigitTens1000[number];
+        }
+    }
+
+    // Collects the 2 (currency) or 3 fractional digits. No rounding.
+    // Starting at startIndex position inclusive. Updates LastUsedIndex.
+    private void collectFractionalDigits(int number, boolean currency,
+                                         char[] digitsBuffer, int startIndex) {
+        lastUsedIndex = currency ? (startIndex + 1) : (startIndex + 2);
+        int index = lastUsedIndex;
+
+        char digitOnes, digitTens;
+        digitOnes = DigitOnes1000[number];
+        digitTens = DigitTens1000[number];
+
+        if (currency) {
+            // Currency case. Always collects fractional digits.
+            digitsBuffer[index]     = digitOnes;
+            digitsBuffer[index - 1] = digitTens;
+        }
+        else {
+            // Decimal case. Don't collect ending zeros.
+            if (number == 0) index -= 4;
+            else {
+                index = index - 2;
+                digitsBuffer[index] = DigitHundreds1000[number];
+                // Takes care of ending zeros that we don't collect.
+                if (digitOnes == '0') {
+                    if (digitTens != '0') {
+                        index = index + 1;
+                        digitsBuffer[index] = digitTens;
+                    }
+                }
+                else {
+                    digitsBuffer[++index] = digitTens;
+                    digitsBuffer[++index] = digitOnes;
+                }
+            }
+            lastUsedIndex = index;
+        }
+    }
+
+    // Fast path format main entry point method.
+    // Formatted result is put in passed internalChars char buffer.
+    // Passed double d must be positive.
+    private int fastDoubleFormat(double d, boolean negative,
+                                 char[] internalChars, int startIndex) {
+        // Extracting integral and fractional part.
+        int integralPartAsInt = (int) d;
+        double fractional = d - (double) integralPartAsInt;
+
+        // Collects integral part infos.
+        int integralLength = intStringSize(integralPartAsInt);
+        boolean integralPartIsAllNines = isAllNines;
+        int nbGroups = nbGroupsNeeded;
+
+        // Collects fractional part infos.
+        isAllNines = false;
+        int fractionalPartAsInt = getFracIntegral(fractional, maximumFractionDigits);
+        int fractionalLength = fracStringSize(fractionalPartAsInt);
+        boolean fractionalPartIsAllNines = isAllNines;
+
+        // Should we round up ?
+        boolean toBeRounded =
+            toBeHalfEvenRounded(d, integralPartAsInt, integralLength,
+                                fractionalPartAsInt, fractionalLength);
+
+        if (!integralPartIsAllNines ||
+            !(fractionalPartIsAllNines && (fractionalLength == maximumFractionDigits))) {
+            int decimalPointIndex    = startIndex + integralLength + nbGroups;
+            int integralLastIndex    = decimalPointIndex - 1;
+            int fractionalStartIndex = decimalPointIndex + 1;
+
+            if (!fractionalPartIsAllNines ||
+                (fractionalLength != maximumFractionDigits)) {
+                // Fractional part to be collected completely in all cases.
+                internalChars[decimalPointIndex] = decimalSeparatorChar;
+                if (toBeRounded) fractionalPartAsInt = fractionalPartAsInt + 1;
+                collectFractionalDigits(fractionalPartAsInt, isCurrencyFormat,
+                                        internalChars, fractionalStartIndex);
+                collectIntegralDigits(integralPartAsInt,
+                                      internalChars, integralLastIndex);
+            }
+            else {
+                // Fractional is "all_nines".
+                // Either we have currency and frac=99, or decimal and frac=999.
+                // Fractional will end up either in 99[9] or 00[0] depending on rounding.
+                if (toBeRounded) {
+                    // Rounding is propagated to integral.
+                    integralPartAsInt = integralPartAsInt + 1;
+
+                    // Collects fractional part.
+                    if (isCurrencyFormat) {
+                        // Fills fractional part with ".00" (pattern rule).
+                        lastUsedIndex = decimalPointIndex + maximumFractionDigits;
+                        internalChars[decimalPointIndex] = decimalSeparatorChar;
+                        internalChars[fractionalStartIndex] = '0';
+                        internalChars[lastUsedIndex] = '0';
+                    }
+                    else {
+                        // Not currency, just forget fractional part (pattern rule).
+                        lastUsedIndex = integralLastIndex;
+                    }
+                }
+                else {
+                    // No rounding up. Fractional remains all_nines (999 or 99) !
+                    internalChars[decimalPointIndex] = decimalSeparatorChar;
+                    fillUpTo4Chars(internalChars, fractionalStartIndex, maximumFractionDigits, '9');
+                    lastUsedIndex = decimalPointIndex + maximumFractionDigits;
+                }
+
+                // Then collects integral part. Already rounded up if needed.
+                collectIntegralDigits(integralPartAsInt, internalChars, integralLastIndex);
+
+            }
+        }
+        else formatAllNinesCase(internalChars, startIndex,
+                                toBeRounded, nbGroups,
+                                integralLength, fractionalLength);
+
+        // Takes into account locale for the digits part only.
+        if (zeroDelta != 0) localizeDigits(internalChars, startIndex);
+
+        // Adds suffix part if needed.
+        if (isSuffixPresent) appendSuffix(negative, internalChars);
+
+        return lastUsedIndex;
+    }
+
+    // A fasthpath shortcut of format(double) to be called by NumberFormat only.
+    //
+    // If instance can be applied fast-path and passed double is not NaN or Infinite,
+    // and is in the integer range, we call directly fastDoubleFormat using the
+    // expected container and prefix position.
+    //
+    // Otherwise returns null since fast-path not possible (convention).
+    //
+    // We check positive and negative zero, minimizing the needed tests
+    // for the most frequent case.
+    String fastFormat(double number) {
+        // Should we check fast path status ?
+        if (fastPathCheckNeeded) checkAndSetFastPathStatus();
+
+        if (isFastPath ) {
+            if (!Double.isNaN(number) &&
+                !Double.isInfinite(number)) {
+
+                int lastIndex;
+                char[] container;
+
+                if (number > 0.0) { // 1 test only for positive doubles
+                    if (number <= MAX_INT_AS_DOUBLE) {
+                        container = positiveFastPathContainer;
+                        lastIndex = fastDoubleFormat(number, false,
+                                                     container, positivePrefix.length());
+                        return new String(container, 0, lastIndex + 1);
+                    }
+                }
+                else if (number < 0.0) { // 2 tests for negative doubles
+                    number = -number;
+                    if (number <= MAX_INT_AS_DOUBLE) {
+                        container = negativeFastPathContainer;
+                        lastIndex = fastDoubleFormat(number, true,
+                                                     container, negativePrefix.length());
+                        return new String(container, 0, lastIndex + 1);
+                    }
+                }
+                else if ( 1/number > 0.0) { // 3 tests for positive zero. Not frequent.
+                    container = positiveFastPathContainer;
+                    lastIndex = fastDoubleFormat(number, false,
+                                                 container, positivePrefix.length());
+                    return new String(container, 0, lastIndex + 1);
+                }
+                else { // 4 tests for negative zero (hopefully very rare)
+                    container = negativeFastPathContainer;
+                    lastIndex = fastDoubleFormat(number, true,
+                                                 container, negativePrefix.length());
+                    return new String(container, 0, lastIndex + 1);
+                }
+            }
+
+            // Fast-path can't be exercized for this passed double !
+            return null;
+        }
+
+        // DecimalFormat instance is not in a fast path state.
+        return null;
+    }
+
+    // End of Fast path formating algorithm for double ====================================================================
+
     /**
      * Complete the formatting of a finite number.  On entry, the digitList must
      * be filled in with the correct digits.
      */
     private StringBuffer subformat(StringBuffer result, FieldDelegate delegate,

@@ -1638,10 +2239,11 @@
     public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) {
         try {
             // don't allow multiple references
             symbols = (DecimalFormatSymbols) newSymbols.clone();
             expandAffixes();
+            fastPathCheckNeeded = true;
         } catch (Exception foo) {
             // should never happen
         }
     }
 

@@ -1659,10 +2261,11 @@
      */
     public void setPositivePrefix (String newValue) {
         positivePrefix = newValue;
         posPrefixPattern = null;
         positivePrefixFieldPositions = null;
+        fastPathCheckNeeded = true;
     }
 
     /**
      * Returns the FieldPositions of the fields in the prefix used for
      * positive numbers. This is not used if the user has explicitly set

@@ -1696,10 +2299,11 @@
      * <P>Examples: -123, ($123) (with negative suffix), sFr-123
      */
     public void setNegativePrefix (String newValue) {
         negativePrefix = newValue;
         negPrefixPattern = null;
+        fastPathCheckNeeded = true;
     }
 
     /**
      * Returns the FieldPositions of the fields in the prefix used for
      * negative numbers. This is not used if the user has explicitly set

@@ -1733,10 +2337,11 @@
      * <P>Example: 123%
      */
     public void setPositiveSuffix (String newValue) {
         positiveSuffix = newValue;
         posSuffixPattern = null;
+        fastPathCheckNeeded = true;
     }
 
     /**
      * Returns the FieldPositions of the fields in the suffix used for
      * positive numbers. This is not used if the user has explicitly set

@@ -1770,10 +2375,11 @@
      * <P>Examples: 123%
      */
     public void setNegativeSuffix (String newValue) {
         negativeSuffix = newValue;
         negSuffixPattern = null;
+        fastPathCheckNeeded = true;
     }
 
     /**
      * Returns the FieldPositions of the fields in the suffix used for
      * negative numbers. This is not used if the user has explicitly set

@@ -1819,13 +2425,23 @@
      */
     public void setMultiplier (int newValue) {
         multiplier = newValue;
         bigDecimalMultiplier = null;
         bigIntegerMultiplier = null;
+        fastPathCheckNeeded = true;
     }
 
     /**
+     * Set whether or not grouping will be used in this format.
+     * @see #isGroupingUsed
+     */
+    public void setGroupingUsed(boolean newValue) {
+        super.setGroupingUsed(newValue);
+        fastPathCheckNeeded = true;
+    }
+
+    /**
      * Return the grouping size. Grouping size is the number of digits between
      * grouping separators in the integer portion of a number.  For example,
      * in the number "123,456.78", the grouping size is 3.
      * @see #setGroupingSize
      * @see java.text.NumberFormat#isGroupingUsed

@@ -1845,10 +2461,11 @@
      * @see java.text.NumberFormat#setGroupingUsed
      * @see java.text.DecimalFormatSymbols#setGroupingSeparator
      */
     public void setGroupingSize (int newValue) {
         groupingSize = (byte)newValue;
+        fastPathCheckNeeded = true;
     }
 
     /**
      * Allows you to get the behavior of the decimal separator with integers.
      * (The decimal separator will always appear with decimals.)

@@ -1863,10 +2480,11 @@
      * (The decimal separator will always appear with decimals.)
      * <P>Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345
      */
     public void setDecimalSeparatorAlwaysShown(boolean newValue) {
         decimalSeparatorAlwaysShown = newValue;
+        fastPathCheckNeeded = true;
     }
 
     /**
      * Returns whether the {@link #parse(java.lang.String, java.text.ParsePosition)}
      * method returns <code>BigDecimal</code>. The default value is false.

@@ -2675,10 +3293,11 @@
         if (minimumIntegerDigits > maximumIntegerDigits) {
             minimumIntegerDigits = maximumIntegerDigits;
             super.setMinimumIntegerDigits((minimumIntegerDigits > DOUBLE_INTEGER_DIGITS) ?
                 DOUBLE_INTEGER_DIGITS : minimumIntegerDigits);
         }
+        fastPathCheckNeeded = true;
     }
 
     /**
      * Sets the minimum number of digits allowed in the integer portion of a
      * number.

@@ -2694,10 +3313,11 @@
         if (minimumIntegerDigits > maximumIntegerDigits) {
             maximumIntegerDigits = minimumIntegerDigits;
             super.setMaximumIntegerDigits((maximumIntegerDigits > DOUBLE_INTEGER_DIGITS) ?
                 DOUBLE_INTEGER_DIGITS : maximumIntegerDigits);
         }
+        fastPathCheckNeeded = true;
     }
 
     /**
      * Sets the maximum number of digits allowed in the fraction portion of a
      * number.

@@ -2713,10 +3333,11 @@
         if (minimumFractionDigits > maximumFractionDigits) {
             minimumFractionDigits = maximumFractionDigits;
             super.setMinimumFractionDigits((minimumFractionDigits > DOUBLE_FRACTION_DIGITS) ?
                 DOUBLE_FRACTION_DIGITS : minimumFractionDigits);
         }
+        fastPathCheckNeeded = true;
     }
 
     /**
      * Sets the minimum number of digits allowed in the fraction portion of a
      * number.

@@ -2732,10 +3353,11 @@
         if (minimumFractionDigits > maximumFractionDigits) {
             maximumFractionDigits = minimumFractionDigits;
             super.setMaximumFractionDigits((maximumFractionDigits > DOUBLE_FRACTION_DIGITS) ?
                 DOUBLE_FRACTION_DIGITS : maximumFractionDigits);
         }
+        fastPathCheckNeeded = true;
     }
 
     /**
      * Gets the maximum number of digits allowed in the integer portion of a
      * number.

@@ -2815,10 +3437,11 @@
             symbols.setCurrency(currency);
             if (isCurrencyFormat) {
                 expandAffixes();
             }
         }
+        fastPathCheckNeeded = true;
     }
 
     /**
      * Gets the {@link java.math.RoundingMode} used in this DecimalFormat.
      *

@@ -2843,10 +3466,11 @@
             throw new NullPointerException();
         }
 
         this.roundingMode = roundingMode;
         digitList.setRoundingMode(roundingMode);
+        fastPathCheckNeeded = true;
     }
 
     /**
      * Adjusts the minimum and maximum fraction digits to values that
      * are reasonable for the currency's default fraction digits.

@@ -3194,10 +3818,56 @@
      * @serial
      * @since 1.6
      */
     private RoundingMode roundingMode = RoundingMode.HALF_EVEN;
 
+    // ------ DecimalFormat fields for Fast Path for double algorithm  ------
+
+    // --- Utility temporary fields only used in fast-path algorithms and shared by several methods.
+
+    /* Flag field for "all nine" integers like 99999999 (see StringSize). */
+    private boolean isAllNines = false;
+
+    /* The fractional part of double that is past 2 (currency) or 3 fractional digits. */
+    private double remainingFractionalPart = 0.0d;
+
+    /* The last valid index in the buffer containing the formated result. */
+    private int lastUsedIndex = 0;
+
+    /* Number of groups needed in formatting the double (see intStringSize and fastDoubleFormat). */
+    private int nbGroupsNeeded = 0;
+
+    // ---  New state fields of DecimalFormat, necessary for fast-path.
+
+    /* The format-fast-path status of the instance. */
+    private boolean isFastPath = false;
+
+    /* Flag stating need of check fast path status on next format call. */
+    private boolean fastPathCheckNeeded = true;
+
+    /* Preinstantiated digits buffer templates for perf purpose (see checkAndSetFastPathStatus). */
+    private char[] positiveFastPathContainer;
+    private char[] negativeFastPathContainer;
+
+    /* Rounding bounds assigned at initialization (see checkAndSetFastPathStatus). */
+    private double[] safeMinusRoundingBounds = null;
+    private double[] safeMajorRoundingBounds = null;
+
+    /* Suffixes recorded as char array (see checkAndSetFastPathStatus). */
+    private boolean isSuffixPresent;
+    private char[] charsPositiveSuffix;
+    private char[] charsNegativeSuffix;
+
+    /* Difference between locale zero and default zero representation (see checkAndSetFastPathStatus). */
+    private int  zeroDelta;
+
+    /* Locale char for grouping separator (see checkAndSetFastPathStatus). */
+    private char groupingChar;
+
+    /* Locale char for decimal separator (see checkAndSetFastPathStatus). */
+    private char decimalSeparatorChar;
+
     //----------------------------------------------------------------------
 
     static final int currentSerialVersion = 4;
 
     /**

@@ -3227,10 +3897,119 @@
 
     //----------------------------------------------------------------------
     // CONSTANTS
     //----------------------------------------------------------------------
 
+    // ------ The Fast Path for double Constants ------
+
+    /* New DecimalFormat static constant */
+    private static double MAX_INT_AS_DOUBLE = (double) Integer.MAX_VALUE;
+
+    /* Digit arrays for collecting digits. */
+    private static final char[] DigitOnes1000 = new char[1000];
+    private static final char[] DigitTens1000 = new char[1000];
+    private static final char[] DigitHundreds1000 = new char[1000];
+
+    static {
+        int tenIndex = 0;
+        int hundredIndex = 0;
+        char digitOne = '0';
+        char digitTen = '0';
+        char digitHundred = '0';
+        for (int i = 0;  i < 1000; i++ ) {
+
+            DigitOnes1000[i] = digitOne;
+            if (digitOne == '9') digitOne = '0';
+            else digitOne++;
+
+            DigitTens1000[i] = digitTen;
+            if (i == (tenIndex + 9)) {
+                tenIndex += 10;
+                if (digitTen == '9') digitTen = '0';
+                else digitTen++;
+            }
+
+            DigitHundreds1000[i] = digitHundred;
+            if (i == (hundredIndex + 99)) {
+                digitHundred++;
+                hundredIndex += 100;
+            }
+        }
+
+    }
+
+    // --- Constant double arrays defining "Undecidable Rounding Interval"
+    private static final double[] StdSafeMajorRoundingBounds = {
+        0.5000000000000005,  // < 0.001d
+        0.5000000000000005,  // < 0.01d
+        0.50000000000002,    // < 0.1d
+        0.50000000000006,    // < 1.0d
+        0.5000000000010,     // < 10.0d
+        0.500000000008,      // < 100.0d
+        0.50000000006,       // < 1000.0d
+        0.5000000010,        // < 10000.0d
+        0.500000008,         // < 100000.0d
+        0.50000006,          // < 1000000.0d
+        0.5000010,           // < 10000000.0d
+        0.500008,            // < 100000000.0d
+        0.50006,             // < 1000000000.0d
+        0.5002               // < (2**31) - 1
+    };
+
+    // SafeMinusRoundingBounds are set as counterpart of SafeMajorRoundingBounds :
+    //  if 0.50000006 for major, then we set to 0.49999994 for minus.
+    private static final double[] StdSafeMinusRoundingBounds = {
+        0.4999999999999995,  // < 0.001d
+        0.4999999999999995,  // < 0.01d
+        0.49999999999998,    // < 0.1d
+        0.49999999999994,    // < 1.0d
+        0.4999999999990,     // < 10.0d
+        0.499999999992,      // < 100.0d
+        0.49999999994,       // < 1000.0d
+        0.4999999990,        // < 10000.0d
+        0.499999992,         // < 100000.0d
+        0.49999994,          // < 1000000.0d
+        0.4999990,           // < 10000000.0d
+        0.499992,            // < 100000000.0d
+        0.49994,             // < 1000000000.0d
+        0.4998               // < (2**31) - 1
+    };
+
+    private static final double[] CurrencySafeMajorRoundingBounds = {
+        0.50000000000000009, // < 0.01d
+        0.5000000000000005,  // < 0.1d
+        0.500000000000008,   // < 1.0d
+        0.50000000000009,    // < 10.0d
+        0.5000000000007,     // < 100.0d
+        0.500000000006,      // < 1000.0d
+        0.50000000009,       // < 10000.0d
+        0.5000000007,        // < 100000.0d
+        0.500000006,         // < 1000000.0d
+        0.50000009,          // < 10000000.0d
+        0.5000008,           // < 100000000.0d
+        0.500006,            // < 1000000000.0d
+        0.50002              // < (2**31) - 1
+    };
+
+    // CurrencySafeMinusRoundingBounds are set as counterpart of CurrencySafeMajorRoundingBounds :
+    //  if 0.50000009 for major, then we set to 0.49999991 for minus. This is because program calculating
+    private static final double[] CurrencySafeMinusRoundingBounds = {
+        0.49999999999999991, // < 0.01d
+        0.4999999999999995,  // < 0.1d
+        0.499999999999992,   // < 1.0d
+        0.49999999999991,    // < 10.0d
+        0.4999999999993,     // < 100.0d
+        0.499999999994,      // < 1000.0d
+        0.49999999991,       // < 10000.0d
+        0.4999999993,        // < 100000.0d
+        0.499999994,         // < 1000000.0d
+        0.49999991,          // < 10000000.0d
+        0.4999992,           // < 100000000.0d
+        0.499994,            // < 1000000000.0d
+        0.49998              // < (2**31) - 1
+    };
+
     // Constants for characters used in programmatic (unlocalized) patterns.
     private static final char       PATTERN_ZERO_DIGIT         = '0';
     private static final char       PATTERN_GROUPING_SEPARATOR = ',';
     private static final char       PATTERN_DECIMAL_SEPARATOR  = '.';
     private static final char       PATTERN_PER_MILLE          = '\u2030';