]> gitweb.fperrin.net Git - Dictionary.git/blobdiff - jars/icu4j-52_1/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
Upgrade ICU4J.
[Dictionary.git] / jars / icu4j-52_1 / main / classes / core / src / com / ibm / icu / text / DecimalFormat.java
similarity index 84%
rename from jars/icu4j-4_8_1_1/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
rename to jars/icu4j-52_1/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
index 4a41229e1be55d5c8678af64f6fd2b78d2b9dea8..03855d0e84508b01d08131449e070eade39037bc 100644 (file)
@@ -1,7 +1,6 @@
-//##header
 /*
  *******************************************************************************
- * Copyright (C) 1996-2011, International Business Machines Corporation and    *
+ * Copyright (C) 1996-2013, International Business Machines Corporation and    *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -28,6 +27,7 @@ import com.ibm.icu.impl.Utility;
 import com.ibm.icu.lang.UCharacter;
 import com.ibm.icu.math.BigDecimal;
 import com.ibm.icu.math.MathContext;
+import com.ibm.icu.text.PluralRules.FixedDecimal;
 import com.ibm.icu.util.Currency;
 import com.ibm.icu.util.CurrencyAmount;
 import com.ibm.icu.util.ULocale;
@@ -342,6 +342,13 @@ import com.ibm.icu.util.ULocale.Category;
  * {@link #parse(String)} indicates parse failure by throwing a {@link
  * java.text.ParseException}.
  *
+ * <p>Parsing an extremely large or small absolute value (such as 1.0E10000 or 1.0E-10000)
+ * requires huge memory allocation for representing the parsed number. Such input may expose
+ * a risk of DoS attacks. To prevent huge memory allocation triggered by such inputs,
+ * <code>DecimalFormat</code> internally limits of maximum decimal digits to be 1000. Thus,
+ * an input string resulting more than 1000 digits in plain decimal representation (non-exponent)
+ * will be treated as either overflow (positive/negative infinite) or underflow (+0.0/-0.0).
+ *
  * <h4>Formatting</h4>
  *
  * <p>Formatting is guided by several parameters, all of which can be specified either
@@ -770,10 +777,52 @@ public class DecimalFormat extends NumberFormat {
      * {@inheritDoc}
      * @stable ICU 2.0
      */
+    @Override
     public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) {
         return format(number, result, fieldPosition, false);
     }
 
+    // See if number is negative.
+    // usage: isNegative(multiply(numberToBeFormatted));
+    private boolean isNegative(double number) {
+        // Detecting whether a double is negative is easy with the exception of the value
+        // -0.0. This is a double which has a zero mantissa (and exponent), but a negative
+        // sign bit. It is semantically distinct from a zero with a positive sign bit, and
+        // this distinction is important to certain kinds of computations. However, it's a
+        // little tricky to detect, since (-0.0 == 0.0) and !(-0.0 < 0.0). How then, you
+        // may ask, does it behave distinctly from +0.0? Well, 1/(-0.0) ==
+        // -Infinity. Proper detection of -0.0 is needed to deal with the issues raised by
+        // bugs 4106658, 4106667, and 4147706. Liu 7/6/98.
+        return (number < 0.0) || (number == 0.0 && 1 / number < 0.0);
+    }
+
+    // Rounds the number and strips of the negative sign.
+    // usage: round(multiply(numberToBeFormatted))
+    private double round(double number) {
+        boolean isNegative = isNegative(number);
+        if (isNegative)
+            number = -number;
+
+        // Apply rounding after multiplier
+        if (roundingDouble > 0.0) {
+            // number = roundingDouble
+            //    * round(number / roundingDouble, roundingMode, isNegative);
+            return round(
+                number, roundingDouble, roundingDoubleReciprocal, roundingMode,
+                isNegative);
+        }
+        return number;
+    }
+
+    // Multiplies given number by multipler (if there is one) returning the new
+    // number. If there is no multiplier, returns the number passed in unchanged.
+    private double multiply(double number) {
+        if (multiplier != 1) {
+            return number * multiplier;
+        }
+        return number;
+    }
+
     // [Spark/CDL] The actual method to format number. If boolean value
     // parseAttr == true, then attribute information will be recorded.
     private StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition,
@@ -805,31 +854,11 @@ public class DecimalFormat extends NumberFormat {
             return result;
         }
 
-        // Do this BEFORE checking to see if value is infinite or negative!
-        if (multiplier != 1)
-            number *= multiplier;
-
-
-        // Detecting whether a double is negative is easy with the exception of the value
-        // -0.0. This is a double which has a zero mantissa (and exponent), but a negative
-        // sign bit. It is semantically distinct from a zero with a positive sign bit, and
-        // this distinction is important to certain kinds of computations. However, it's a
-        // little tricky to detect, since (-0.0 == 0.0) and !(-0.0 < 0.0). How then, you
-        // may ask, does it behave distinctly from +0.0? Well, 1/(-0.0) ==
-        // -Infinity. Proper detection of -0.0 is needed to deal with the issues raised by
-        // bugs 4106658, 4106667, and 4147706. Liu 7/6/98.
-        boolean isNegative = (number < 0.0) || (number == 0.0 && 1 / number < 0.0);
-        if (isNegative)
-            number = -number;
-
-        // Apply rounding after multiplier
-        if (roundingDouble > 0.0) {
-            // number = roundingDouble
-            //    * round(number / roundingDouble, roundingMode, isNegative);
-            double newNumber = round(number, roundingDouble, roundingDoubleReciprocal, roundingMode,
-                                     isNegative);
-            number = newNumber;
-        }
+        // Do this BEFORE checking to see if value is negative or infinite and
+        // before rounding.
+        number = multiply(number);
+        boolean isNegative = isNegative(number);
+        number = round(number);
 
         if (Double.isInfinite(number)) {
             int prefixLen = appendAffix(result, isNegative, true, parseAttr);
@@ -867,6 +896,54 @@ public class DecimalFormat extends NumberFormat {
         }
     }
 
+    /**
+     * This is a special function used by the CompactDecimalFormat subclass.
+     * It completes only the rounding portion of the formatting and returns
+     * the resulting double. CompactDecimalFormat uses the result to compute
+     * the plural form to use.
+     *
+     * @param number The number to format.
+     * @return The number rounded to the correct number of significant digits
+     * with negative sign stripped off.
+     * @internal
+     * @deprecated
+     */
+    @Deprecated
+    double adjustNumberAsInFormatting(double number) {
+        if (Double.isNaN(number)) {
+            return number;
+        }
+        number = round(multiply(number));
+        if (Double.isInfinite(number)) {
+            return number;
+        }
+        return toDigitList(number).getDouble();
+    }
+    
+    @Deprecated
+    DigitList toDigitList(double number) {
+        DigitList result = new DigitList();
+        result.set(number, precision(false), false);
+        return result;
+    }
+
+    /**
+      * This is a special function used by the CompactDecimalFormat subclass
+      * to determine if the number to be formatted is negative.
+      *
+      * @param number The number to format.
+      * @return True if number is negative.
+      * @internal
+      * @deprecated
+      */
+     @Deprecated
+     boolean isNumberNegative(double number) {
+         if (Double.isNaN(number)) {
+             return false;
+         }
+         return isNegative(multiply(number));
+     }
+
     /**
      * Round a double value to the nearest multiple of the given rounding increment,
      * according to the given mode. This is equivalent to rounding value/roundingInc to
@@ -966,6 +1043,7 @@ public class DecimalFormat extends NumberFormat {
      * @stable ICU 2.0
      */
     // [Spark/CDL] Delegate to format_long_StringBuffer_FieldPosition_boolean
+    @Override
     public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) {
         return format(number, result, fieldPosition, false);
     }
@@ -977,7 +1055,7 @@ public class DecimalFormat extends NumberFormat {
 
         // If we are to do rounding, we need to move into the BigDecimal
         // domain in order to do divide/multiply correctly.
-        if (roundingIncrementICU != null) {
+        if (actualRoundingIncrementICU != null) {
             return format(BigDecimal.valueOf(number), result, fieldPosition);
         }
 
@@ -1020,6 +1098,7 @@ public class DecimalFormat extends NumberFormat {
      *
      * @stable ICU 2.0
      */
+    @Override
     public StringBuffer format(BigInteger number, StringBuffer result,
                                FieldPosition fieldPosition) {
         return format(number, result, fieldPosition, false);
@@ -1029,7 +1108,7 @@ public class DecimalFormat extends NumberFormat {
                                 boolean parseAttr) {
         // If we are to do rounding, we need to move into the BigDecimal
         // domain in order to do divide/multiply correctly.
-        if (roundingIncrementICU != null) {
+        if (actualRoundingIncrementICU != null) {
             return format(new BigDecimal(number), result, fieldPosition);
         }
 
@@ -1051,6 +1130,7 @@ public class DecimalFormat extends NumberFormat {
      *
      * @stable ICU 2.0
      */
+    @Override
     public StringBuffer format(java.math.BigDecimal number, StringBuffer result,
                                FieldPosition fieldPosition) {
         return format(number, result, fieldPosition, false);
@@ -1063,8 +1143,8 @@ public class DecimalFormat extends NumberFormat {
             number = number.multiply(java.math.BigDecimal.valueOf(multiplier));
         }
 
-        if (roundingIncrement != null) {
-            number = number.divide(roundingIncrement, 0, roundingMode).multiply(roundingIncrement);
+        if (actualRoundingIncrement != null) {
+            number = number.divide(actualRoundingIncrement, 0, roundingMode).multiply(actualRoundingIncrement);
         }
 
         synchronized (digitList) {
@@ -1080,6 +1160,7 @@ public class DecimalFormat extends NumberFormat {
      *
      * @stable ICU 2.0
      */
+    @Override
     public StringBuffer format(BigDecimal number, StringBuffer result,
                                FieldPosition fieldPosition) {
          // This method is just a copy of the corresponding java.math.BigDecimal method
@@ -1090,9 +1171,9 @@ public class DecimalFormat extends NumberFormat {
             number = number.multiply(BigDecimal.valueOf(multiplier), mathContext);
         }
 
-        if (roundingIncrementICU != null) {
-            number = number.divide(roundingIncrementICU, 0, roundingMode)
-                .multiply(roundingIncrementICU, mathContext);
+        if (actualRoundingIncrementICU != null) {
+            number = number.divide(actualRoundingIncrementICU, 0, roundingMode)
+                .multiply(actualRoundingIncrementICU, mathContext);
         }
 
         synchronized (digitList) {
@@ -1141,18 +1222,68 @@ public class DecimalFormat extends NumberFormat {
     private StringBuffer subformat(int number, StringBuffer result, FieldPosition fieldPosition,
                                    boolean isNegative, boolean isInteger, boolean parseAttr) {
         if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
-            return subformat(currencyPluralInfo.select(number), result, fieldPosition, isNegative,
+            // compute the plural category from the digitList plus other settings
+            return subformat(currencyPluralInfo.select(getFixedDecimal(number)),
+                             result, fieldPosition, isNegative,
                              isInteger, parseAttr);
         } else {
             return subformat(result, fieldPosition, isNegative, isInteger, parseAttr);
         }
     }
 
+    /**
+     * This is ugly, but don't see a better way to do it without major restructuring of the code.
+     */
+    /*package*/ FixedDecimal getFixedDecimal(double number) {
+        // get the visible fractions and the number of fraction digits.
+       return getFixedDecimal(number, digitList);
+    }
+    
+    FixedDecimal getFixedDecimal(double number, DigitList dl) {
+        int fractionalDigitsInDigitList = dl.count - dl.decimalAt;
+        int v;
+        long f;
+        int maxFractionalDigits;
+        int minFractionalDigits;
+        if (useSignificantDigits) {
+            maxFractionalDigits = maxSignificantDigits - dl.decimalAt;
+            minFractionalDigits = minSignificantDigits - dl.decimalAt;
+            if (minFractionalDigits < 0) {
+                minFractionalDigits = 0;
+            }
+            if (maxFractionalDigits < 0) {
+                maxFractionalDigits = 0;
+            }
+        } else {
+            maxFractionalDigits = getMaximumFractionDigits();
+            minFractionalDigits = getMinimumFractionDigits();
+        }
+        v = fractionalDigitsInDigitList;
+        if (v < minFractionalDigits) {
+            v = minFractionalDigits;
+        } else if (v > maxFractionalDigits) {
+            v = maxFractionalDigits;
+        }
+        f = 0;
+        if (v > 0) {
+            for (int i = Math.max(0, dl.decimalAt); i < dl.count; ++i) {
+                f *= 10;
+                f += (dl.digits[i] - '0');
+            }
+            for (int i = v; i < fractionalDigitsInDigitList; ++i) {
+                f *= 10;
+            }
+        }
+        return new FixedDecimal(number, v, f);
+    }
+
     private StringBuffer subformat(double number, StringBuffer result, FieldPosition fieldPosition,
                                    boolean isNegative,
             boolean isInteger, boolean parseAttr) {
         if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
-            return subformat(currencyPluralInfo.select(number), result, fieldPosition, isNegative,
+            // compute the plural category from the digitList plus other settings
+            return subformat(currencyPluralInfo.select(getFixedDecimal(number)),
+                             result, fieldPosition, isNegative,
                              isInteger, parseAttr);
         } else {
             return subformat(result, fieldPosition, isNegative, isInteger, parseAttr);
@@ -1209,16 +1340,7 @@ public class DecimalFormat extends NumberFormat {
         // digitList.count = 0;
         // }
 
-        int i;
-        char [] digits = symbols.getDigitsLocal();
-        
-        char grouping = currencySignCount > 0 ? symbols.getMonetaryGroupingSeparator() :
-            symbols.getGroupingSeparator();
-        char decimal = currencySignCount > 0 ? symbols.getMonetaryDecimalSeparator() :
-            symbols.getDecimalSeparator();
-        boolean useSigDig = areSignificantDigitsUsed();
-        int maxIntDig = getMaximumIntegerDigits();
-        int minIntDig = getMinimumIntegerDigits();
+
 
         // Per bug 4147706, DecimalFormat must respect the sign of numbers which format as
         // zero. This allows sensible computations and preserves relations such as
@@ -1231,362 +1353,440 @@ public class DecimalFormat extends NumberFormat {
         int prefixLen = appendAffix(result, isNegative, true, parseAttr);
 
         if (useExponentialNotation) {
-            // Record field information for caller.
-            if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
-                fieldPosition.setBeginIndex(result.length());
-                fieldPosition.setEndIndex(-1);
-            } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
-                fieldPosition.setBeginIndex(-1);
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
-                fieldPosition.setBeginIndex(result.length());
-                fieldPosition.setEndIndex(-1);
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
-                fieldPosition.setBeginIndex(-1);
-            }
+            subformatExponential(result, fieldPosition, parseAttr);
+        } else {
+            subformatFixed(result, fieldPosition, isInteger, parseAttr);
+        }
 
-            // [Spark/CDL]
-            // the begin index of integer part
-            // the end index of integer part
-            // the begin index of fractional part
-            int intBegin = result.length();
-            int intEnd = -1;
-            int fracBegin = -1;
-            int minFracDig = 0;
-            if (useSigDig) {
-                maxIntDig = minIntDig = 1;
-                minFracDig = getMinimumSignificantDigits() - 1;
-            } else {
-                minFracDig = getMinimumFractionDigits();
-                if (maxIntDig > MAX_SCIENTIFIC_INTEGER_DIGITS) {
-                    maxIntDig = 1;
-                    if (maxIntDig < minIntDig) {
-                        maxIntDig = minIntDig;
-                    }
-                }
-                if (maxIntDig > minIntDig) {
-                    minIntDig = 1;
-                }
-            }
+        int suffixLen = appendAffix(result, isNegative, false, parseAttr);
 
-            // Minimum integer digits are handled in exponential format by adjusting the
-            // exponent. For example, 0.01234 with 3 minimum integer digits is "123.4E-4".
+        addPadding(result, fieldPosition, prefixLen, suffixLen);
+        return result;
+    }
 
-            // Maximum integer digits are interpreted as indicating the repeating
-            // range. This is useful for engineering notation, in which the exponent is
-            // restricted to a multiple of 3. For example, 0.01234 with 3 maximum integer
-            // digits is "12.34e-3".  If maximum integer digits are defined and are larger
-            // than minimum integer digits, then minimum integer digits are ignored.
+    private void subformatFixed(StringBuffer result,
+            FieldPosition fieldPosition,
+            boolean isInteger,
+            boolean parseAttr) {
+        char [] digits = symbols.getDigitsLocal();
 
-            int exponent = digitList.decimalAt;
-            if (maxIntDig > 1 && maxIntDig != minIntDig) {
-                // A exponent increment is defined; adjust to it.
-                exponent = (exponent > 0) ? (exponent - 1) / maxIntDig : (exponent / maxIntDig) - 1;
-                exponent *= maxIntDig;
+        char grouping = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ?
+                symbols.getGroupingSeparator(): symbols.getMonetaryGroupingSeparator();
+        char decimal = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ?
+                symbols.getDecimalSeparator() : symbols.getMonetaryDecimalSeparator();
+        boolean useSigDig = areSignificantDigitsUsed();
+        int maxIntDig = getMaximumIntegerDigits();
+        int minIntDig = getMinimumIntegerDigits();
+        int i;
+        // [Spark/CDL] Record the integer start index.
+        int intBegin = result.length();
+        // Record field information for caller.
+        if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+            fieldPosition.setBeginIndex(result.length());
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+            fieldPosition.setBeginIndex(result.length());
+        }
+        long fractionalDigits = 0;
+        int fractionalDigitsCount = 0;
+        boolean recordFractionDigits = false;
+
+        int sigCount = 0;
+        int minSigDig = getMinimumSignificantDigits();
+        int maxSigDig = getMaximumSignificantDigits();
+        if (!useSigDig) {
+            minSigDig = 0;
+            maxSigDig = Integer.MAX_VALUE;
+        }
+
+        // Output the integer portion. Here 'count' is the total number of integer
+        // digits we will display, including both leading zeros required to satisfy
+        // getMinimumIntegerDigits, and actual digits present in the number.
+        int count = useSigDig ? Math.max(1, digitList.decimalAt) : minIntDig;
+        if (digitList.decimalAt > 0 && count < digitList.decimalAt) {
+            count = digitList.decimalAt;
+        }
+
+        // Handle the case where getMaximumIntegerDigits() is smaller than the real
+        // number of integer digits. If this is so, we output the least significant
+        // max integer digits. For example, the value 1997 printed with 2 max integer
+        // digits is just "97".
+
+        int digitIndex = 0; // Index into digitList.fDigits[]
+        if (count > maxIntDig && maxIntDig >= 0) {
+            count = maxIntDig;
+            digitIndex = digitList.decimalAt - count;
+        }
+
+        int sizeBeforeIntegerPart = result.length();
+        for (i = count - 1; i >= 0; --i) {
+            if (i < digitList.decimalAt && digitIndex < digitList.count
+                && sigCount < maxSigDig) {
+                // Output a real digit
+                result.append(digits[digitList.getDigitValue(digitIndex++)]);
+                ++sigCount;
             } else {
-                // No exponent increment is defined; use minimum integer digits.
-                // If none is specified, as in "#E0", generate 1 integer digit.
-                exponent -= (minIntDig > 0 || minFracDig > 0) ? minIntDig : 1;
-            }
-
-            // We now output a minimum number of digits, and more if there are more
-            // digits, up to the maximum number of digits. We place the decimal point
-            // after the "integer" digits, which are the first (decimalAt - exponent)
-            // digits.
-            int minimumDigits = minIntDig + minFracDig;
-            // The number of integer digits is handled specially if the number
-            // is zero, since then there may be no digits.
-            int integerDigits = digitList.isZero() ? minIntDig : digitList.decimalAt - exponent;
-            int totalDigits = digitList.count;
-            if (minimumDigits > totalDigits)
-                totalDigits = minimumDigits;
-            if (integerDigits > totalDigits)
-                totalDigits = integerDigits;
-
-            for (i = 0; i < totalDigits; ++i) {
-                if (i == integerDigits) {
-                    // Record field information for caller.
-                    if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
-                        fieldPosition.setEndIndex(result.length());
-                    } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
-                        fieldPosition.setEndIndex(result.length());
-                    }
-
-                    // [Spark/CDL] Add attribute for integer part
-                    if (parseAttr) {
-                        intEnd = result.length();
-                        addAttribute(Field.INTEGER, intBegin, result.length());
-                    }
-                    result.append(decimal);
-                    // [Spark/CDL] Add attribute for decimal separator
-                    if (parseAttr) {
-                        // Length of decimal separator is 1.
-                        int decimalSeparatorBegin = result.length() - 1;
-                        addAttribute(Field.DECIMAL_SEPARATOR, decimalSeparatorBegin,
-                                     result.length());
-                        fracBegin = result.length();
-                    }
-                    // Record field information for caller.
-                    if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
-                        fieldPosition.setBeginIndex(result.length());
-                    } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
-                        fieldPosition.setBeginIndex(result.length());
-                    }
-                }
-                result.append((i < digitList.count)
-                              ? digits[digitList.getDigitValue(i)]
-                              : digits[0]);
-            }
-
-            // For ICU compatibility and format 0 to 0E0 with pattern "#E0" [Richard/GCL]
-            if (digitList.isZero() && (totalDigits == 0)) {
+                // Output a zero (leading or trailing)
                 result.append(digits[0]);
-            }
-
-            // Record field information
-            if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
-                if (fieldPosition.getEndIndex() < 0) {
-                    fieldPosition.setEndIndex(result.length());
-                }
-            } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
-                if (fieldPosition.getBeginIndex() < 0) {
-                    fieldPosition.setBeginIndex(result.length());
-                }
-                fieldPosition.setEndIndex(result.length());
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
-                if (fieldPosition.getEndIndex() < 0) {
-                    fieldPosition.setEndIndex(result.length());
-                }
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
-                if (fieldPosition.getBeginIndex() < 0) {
-                    fieldPosition.setBeginIndex(result.length());
-                }
-                fieldPosition.setEndIndex(result.length());
-            }
-
-            // [Spark/CDL] Calcuate the end index of integer part and fractional
-            // part if they are not properly processed yet.
-            if (parseAttr) {
-                if (intEnd < 0) {
-                    addAttribute(Field.INTEGER, intBegin, result.length());
-                }
-                if (fracBegin > 0) {
-                    addAttribute(Field.FRACTION, fracBegin, result.length());
+                if (sigCount > 0) {
+                    ++sigCount;
                 }
             }
 
-            // The exponent is output using the pattern-specified minimum exponent
-            // digits. There is no maximum limit to the exponent digits, since truncating
-            // the exponent would result in an unacceptable inaccuracy.
-            result.append(symbols.getExponentSeparator());
-            // [Spark/CDL] For exponent symbol, add an attribute.
-            if (parseAttr) {
-                addAttribute(Field.EXPONENT_SYMBOL, result.length() -
-                             symbols.getExponentSeparator().length(), result.length());
-            }
-            // For zero values, we force the exponent to zero. We must do this here, and
-            // not earlier, because the value is used to determine integer digit count
-            // above.
-            if (digitList.isZero())
-                exponent = 0;
-
-            boolean negativeExponent = exponent < 0;
-            if (negativeExponent) {
-                exponent = -exponent;
-                result.append(symbols.getMinusSign());
-                // [Spark/CDL] If exponent has sign, then add an exponent sign
-                // attribute.
-                if (parseAttr) {
-                    // Length of exponent sign is 1.
-                    addAttribute(Field.EXPONENT_SIGN, result.length() - 1, result.length());
-                }
-            } else if (exponentSignAlwaysShown) {
-                result.append(symbols.getPlusSign());
-                // [Spark/CDL] Add an plus sign attribute.
+            // Output grouping separator if necessary.
+            if (isGroupingPosition(i)) {
+                result.append(grouping);
+                // [Spark/CDL] Add grouping separator attribute here.
                 if (parseAttr) {
-                    // Length of exponent sign is 1.
-                    int expSignBegin = result.length() - 1;
-                    addAttribute(Field.EXPONENT_SIGN, expSignBegin, result.length());
+                    // Length of grouping separator is 1.
+                    addAttribute(Field.GROUPING_SEPARATOR, result.length() - 1, result.length());
                 }
             }
-            int expBegin = result.length();
-            digitList.set(exponent);
-            {
-                int expDig = minExponentDigits;
-                if (useExponentialNotation && expDig < 1) {
-                    expDig = 1;
-                }
-                for (i = digitList.decimalAt; i < expDig; ++i)
-                    result.append(digits[0]);
+        }
+
+        // Record field information for caller.
+        if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+            fieldPosition.setEndIndex(result.length());
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+            fieldPosition.setEndIndex(result.length());
+        }
+        
+        // This handles the special case of formatting 0. For zero only, we count the
+        // zero to the left of the decimal point as one signficant digit. Ordinarily we
+        // do not count any leading 0's as significant. If the number we are formatting
+        // is not zero, then either sigCount or digits.getCount() will be non-zero.
+        if (sigCount == 0 && digitList.count == 0) {
+          sigCount = 1;
+        }      
+
+        // Determine whether or not there are any printable fractional digits. If
+        // we've used up the digits we know there aren't.
+        boolean fractionPresent = (!isInteger && digitIndex < digitList.count)
+                || (useSigDig ? (sigCount < minSigDig) : (getMinimumFractionDigits() > 0));
+
+        // If there is no fraction present, and we haven't printed any integer digits,
+        // then print a zero. Otherwise we won't print _any_ digits, and we won't be
+        // able to parse this string.
+        if (!fractionPresent && result.length() == sizeBeforeIntegerPart)
+            result.append(digits[0]);
+        // [Spark/CDL] Add attribute for integer part.
+        if (parseAttr) {
+            addAttribute(Field.INTEGER, intBegin, result.length());
+        }
+        // Output the decimal separator if we always do so.
+        if (decimalSeparatorAlwaysShown || fractionPresent) {
+            if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) {
+                fieldPosition.setBeginIndex(result.length());
             }
-            for (i = 0; i < digitList.decimalAt; ++i) {
-                result.append((i < digitList.count) ? digits[digitList.getDigitValue(i)]
-                              : digits[0]);
+            result.append(decimal);
+            if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) {
+                fieldPosition.setEndIndex(result.length());
             }
-            // [Spark/CDL] Add attribute for exponent part.
+            // [Spark/CDL] Add attribute for decimal separator
             if (parseAttr) {
-                addAttribute(Field.EXPONENT, expBegin, result.length());
-            }
-        } else {
-            // [Spark/CDL] Record the integer start index.
-            int intBegin = result.length();
-            // Record field information for caller.
-            if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
-                fieldPosition.setBeginIndex(result.length());
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
-                fieldPosition.setBeginIndex(result.length());
-            }
-
-            int sigCount = 0;
-            int minSigDig = getMinimumSignificantDigits();
-            int maxSigDig = getMaximumSignificantDigits();
-            if (!useSigDig) {
-                minSigDig = 0;
-                maxSigDig = Integer.MAX_VALUE;
+                addAttribute(Field.DECIMAL_SEPARATOR, result.length() - 1, result.length());
             }
+        }
 
-            // Output the integer portion. Here 'count' is the total number of integer
-            // digits we will display, including both leading zeros required to satisfy
-            // getMinimumIntegerDigits, and actual digits present in the number.
-            int count = useSigDig ? Math.max(1, digitList.decimalAt) : minIntDig;
-            if (digitList.decimalAt > 0 && count < digitList.decimalAt) {
-                count = digitList.decimalAt;
-            }
+        // Record field information for caller.
+        if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+            fieldPosition.setBeginIndex(result.length());
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+            fieldPosition.setBeginIndex(result.length());
+        }
 
-            // Handle the case where getMaximumIntegerDigits() is smaller than the real
-            // number of integer digits. If this is so, we output the least significant
-            // max integer digits. For example, the value 1997 printed with 2 max integer
-            // digits is just "97".
+        // [Spark/CDL] Record the begin index of fraction part.
+        int fracBegin = result.length();
+        recordFractionDigits = fieldPosition instanceof UFieldPosition;
 
-            int digitIndex = 0; // Index into digitList.fDigits[]
-            if (count > maxIntDig && maxIntDig >= 0) {
-                count = maxIntDig;
-                digitIndex = digitList.decimalAt - count;
+        count = useSigDig ? Integer.MAX_VALUE : getMaximumFractionDigits();
+        if (useSigDig && (sigCount == maxSigDig ||
+                          (sigCount >= minSigDig && digitIndex == digitList.count))) {
+            count = 0;
+        }
+        for (i = 0; i < count; ++i) {
+            // Here is where we escape from the loop. We escape if we've output the
+            // maximum fraction digits (specified in the for expression above). We
+            // also stop when we've output the minimum digits and either: we have an
+            // integer, so there is no fractional stuff to display, or we're out of
+            // significant digits.
+            if (!useSigDig && i >= getMinimumFractionDigits() &&
+                (isInteger || digitIndex >= digitList.count)) {
+                break;
             }
 
-            int sizeBeforeIntegerPart = result.length();
-            for (i = count - 1; i >= 0; --i) {
-                if (i < digitList.decimalAt && digitIndex < digitList.count
-                    && sigCount < maxSigDig) {
-                    // Output a real digit
-                    result.append(digits[digitList.getDigitValue(digitIndex++)]);
-                    ++sigCount;
-                } else {
-                    // Output a zero (leading or trailing)
-                    result.append(digits[0]);
-                    if (sigCount > 0) {
-                        ++sigCount;
-                    }
+            // Output leading fractional zeros. These are zeros that come after the
+            // decimal but before any significant digits. These are only output if
+            // abs(number being formatted) < 1.0.
+            if (-1 - i > (digitList.decimalAt - 1)) {
+                result.append(digits[0]);
+                if (recordFractionDigits) {
+                    ++fractionalDigitsCount;
+                    fractionalDigits *= 10;
                 }
+                continue;
+            }
 
-                // Output grouping separator if necessary.
-                if (isGroupingPosition(i)) {
-                    result.append(grouping);
-                    // [Spark/CDL] Add grouping separator attribute here.
-                    if (parseAttr) {
-                        // Length of grouping separator is 1.
-                        addAttribute(Field.GROUPING_SEPARATOR, result.length() - 1, result.length());
-                    }
+            // Output a digit, if we have any precision left, or a zero if we
+            // don't. We don't want to output noise digits.
+            if (!isInteger && digitIndex < digitList.count) {
+                byte digit = digitList.getDigitValue(digitIndex++);
+                result.append(digits[digit]);
+                if (recordFractionDigits) {
+                    ++fractionalDigitsCount;
+                    fractionalDigits *= 10;
+                    fractionalDigits += digit;
+                }
+            } else {
+                result.append(digits[0]);
+                if (recordFractionDigits) {
+                    ++fractionalDigitsCount;
+                    fractionalDigits *= 10;
                 }
             }
 
-            // Record field information for caller.
-            if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
-                fieldPosition.setEndIndex(result.length());
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
-                fieldPosition.setEndIndex(result.length());
+            // If we reach the maximum number of significant digits, or if we output
+            // all the real digits and reach the minimum, then we are done.
+            ++sigCount;
+            if (useSigDig && (sigCount == maxSigDig ||
+                              (digitIndex == digitList.count && sigCount >= minSigDig))) {
+                break;
             }
+        }
 
-            // Determine whether or not there are any printable fractional digits. If
-            // we've used up the digits we know there aren't.
-            boolean fractionPresent = (!isInteger && digitIndex < digitList.count)
-                    || (useSigDig ? (sigCount < minSigDig) : (getMinimumFractionDigits() > 0));
+        // Record field information for caller.
+        if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+            fieldPosition.setEndIndex(result.length());
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+            fieldPosition.setEndIndex(result.length());
+        }
+        if (recordFractionDigits) {
+            ((UFieldPosition) fieldPosition).setFractionDigits(fractionalDigitsCount, fractionalDigits);
+        }
 
-            // If there is no fraction present, and we haven't printed any integer digits,
-            // then print a zero. Otherwise we won't print _any_ digits, and we won't be
-            // able to parse this string.
-            if (!fractionPresent && result.length() == sizeBeforeIntegerPart)
-                result.append(digits[0]);
-            // [Spark/CDL] Add attribute for integer part.
-            if (parseAttr) {
-                addAttribute(Field.INTEGER, intBegin, result.length());
-            }
-            // Output the decimal separator if we always do so.
-            if (decimalSeparatorAlwaysShown || fractionPresent) {
-                result.append(decimal);
-                // [Spark/CDL] Add attribute for decimal separator
-                if (parseAttr) {
-                    addAttribute(Field.DECIMAL_SEPARATOR, result.length() - 1, result.length());
+        // [Spark/CDL] Add attribute information if necessary.
+        if (parseAttr && (decimalSeparatorAlwaysShown || fractionPresent)) {
+            addAttribute(Field.FRACTION, fracBegin, result.length());
+        }
+    }
+
+    private void subformatExponential(StringBuffer result,
+            FieldPosition fieldPosition,
+            boolean parseAttr) {
+        char [] digits = symbols.getDigitsLocal();
+        char decimal = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ?
+                symbols.getDecimalSeparator() : symbols.getMonetaryDecimalSeparator();
+        boolean useSigDig = areSignificantDigitsUsed();
+        int maxIntDig = getMaximumIntegerDigits();
+        int minIntDig = getMinimumIntegerDigits();
+        int i;
+        // Record field information for caller.
+        if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+            fieldPosition.setBeginIndex(result.length());
+            fieldPosition.setEndIndex(-1);
+        } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+            fieldPosition.setBeginIndex(-1);
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+            fieldPosition.setBeginIndex(result.length());
+            fieldPosition.setEndIndex(-1);
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+            fieldPosition.setBeginIndex(-1);
+        }
+
+
+        // [Spark/CDL]
+        // the begin index of integer part
+        // the end index of integer part
+        // the begin index of fractional part
+        int intBegin = result.length();
+        int intEnd = -1;
+        int fracBegin = -1;
+        int minFracDig = 0;
+        if (useSigDig) {
+            maxIntDig = minIntDig = 1;
+            minFracDig = getMinimumSignificantDigits() - 1;
+        } else {
+            minFracDig = getMinimumFractionDigits();
+            if (maxIntDig > MAX_SCIENTIFIC_INTEGER_DIGITS) {
+                maxIntDig = 1;
+                if (maxIntDig < minIntDig) {
+                    maxIntDig = minIntDig;
                 }
             }
-
-            // Record field information for caller.
-            if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
-                fieldPosition.setBeginIndex(result.length());
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
-                fieldPosition.setBeginIndex(result.length());
+            if (maxIntDig > minIntDig) {
+                minIntDig = 1;
             }
+        }
+        long fractionalDigits = 0;
+        int fractionalDigitsCount = 0;
+        boolean recordFractionDigits = false;
 
-            // [Spark/CDL] Record the begin index of fraction part.
-            int fracBegin = result.length();
+        // Minimum integer digits are handled in exponential format by adjusting the
+        // exponent. For example, 0.01234 with 3 minimum integer digits is "123.4E-4".
 
-            count = useSigDig ? Integer.MAX_VALUE : getMaximumFractionDigits();
-            if (useSigDig && (sigCount == maxSigDig ||
-                              (sigCount >= minSigDig && digitIndex == digitList.count))) {
-                count = 0;
-            }
-            for (i = 0; i < count; ++i) {
-                // Here is where we escape from the loop. We escape if we've output the
-                // maximum fraction digits (specified in the for expression above). We
-                // also stop when we've output the minimum digits and either: we have an
-                // integer, so there is no fractional stuff to display, or we're out of
-                // significant digits.
-                if (!useSigDig && i >= getMinimumFractionDigits() &&
-                    (isInteger || digitIndex >= digitList.count)) {
-                    break;
-                }
+        // Maximum integer digits are interpreted as indicating the repeating
+        // range. This is useful for engineering notation, in which the exponent is
+        // restricted to a multiple of 3. For example, 0.01234 with 3 maximum integer
+        // digits is "12.34e-3".  If maximum integer digits are defined and are larger
+        // than minimum integer digits, then minimum integer digits are ignored.
 
-                // Output leading fractional zeros. These are zeros that come after the
-                // decimal but before any significant digits. These are only output if
-                // abs(number being formatted) < 1.0.
-                if (-1 - i > (digitList.decimalAt - 1)) {
-                    result.append(digits[0]);
-                    continue;
+        int exponent = digitList.decimalAt;
+        if (maxIntDig > 1 && maxIntDig != minIntDig) {
+            // A exponent increment is defined; adjust to it.
+            exponent = (exponent > 0) ? (exponent - 1) / maxIntDig : (exponent / maxIntDig) - 1;
+            exponent *= maxIntDig;
+        } else {
+            // No exponent increment is defined; use minimum integer digits.
+            // If none is specified, as in "#E0", generate 1 integer digit.
+            exponent -= (minIntDig > 0 || minFracDig > 0) ? minIntDig : 1;
+        }
+
+        // We now output a minimum number of digits, and more if there are more
+        // digits, up to the maximum number of digits. We place the decimal point
+        // after the "integer" digits, which are the first (decimalAt - exponent)
+        // digits.
+        int minimumDigits = minIntDig + minFracDig;
+        // The number of integer digits is handled specially if the number
+        // is zero, since then there may be no digits.
+        int integerDigits = digitList.isZero() ? minIntDig : digitList.decimalAt - exponent;
+        int totalDigits = digitList.count;
+        if (minimumDigits > totalDigits)
+            totalDigits = minimumDigits;
+        if (integerDigits > totalDigits)
+            totalDigits = integerDigits;
+
+        for (i = 0; i < totalDigits; ++i) {
+            if (i == integerDigits) {
+                // Record field information for caller.
+                if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+                    fieldPosition.setEndIndex(result.length());
+                } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+                    fieldPosition.setEndIndex(result.length());
                 }
 
-                // Output a digit, if we have any precision left, or a zero if we
-                // don't. We don't want to output noise digits.
-                if (!isInteger && digitIndex < digitList.count) {
-                    result.append(digits[digitList.getDigitValue(digitIndex++)]);
-                } else {
-                    result.append(digits[0]);
+                // [Spark/CDL] Add attribute for integer part
+                if (parseAttr) {
+                    intEnd = result.length();
+                    addAttribute(Field.INTEGER, intBegin, result.length());
                 }
-
-                // If we reach the maximum number of significant digits, or if we output
-                // all the real digits and reach the minimum, then we are done.
-                ++sigCount;
-                if (useSigDig && (sigCount == maxSigDig ||
-                                  (digitIndex == digitList.count && sigCount >= minSigDig))) {
-                    break;
+                result.append(decimal);
+                // [Spark/CDL] Add attribute for decimal separator
+                if (parseAttr) {
+                    // Length of decimal separator is 1.
+                    int decimalSeparatorBegin = result.length() - 1;
+                    addAttribute(Field.DECIMAL_SEPARATOR, decimalSeparatorBegin,
+                                 result.length());
+                    fracBegin = result.length();
+                }
+                // Record field information for caller.
+                if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+                    fieldPosition.setBeginIndex(result.length());
+                } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+                    fieldPosition.setBeginIndex(result.length());
                 }
+                recordFractionDigits = fieldPosition instanceof UFieldPosition;
+
+            }
+            byte digit = (i < digitList.count) ? digitList.getDigitValue(i) : (byte)0;
+            result.append(digits[digit]);
+            if (recordFractionDigits) {
+                ++fractionalDigitsCount;
+                fractionalDigits *= 10;
+                fractionalDigits += digit;
             }
+        }
+
+        // For ICU compatibility and format 0 to 0E0 with pattern "#E0" [Richard/GCL]
+        if (digitList.isZero() && (totalDigits == 0)) {
+            result.append(digits[0]);
+        }
 
-            // Record field information for caller.
-            if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+        // Record field information
+        if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+            if (fieldPosition.getEndIndex() < 0) {
                 fieldPosition.setEndIndex(result.length());
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+            }
+        } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+            if (fieldPosition.getBeginIndex() < 0) {
+                fieldPosition.setBeginIndex(result.length());
+            }
+            fieldPosition.setEndIndex(result.length());
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+            if (fieldPosition.getEndIndex() < 0) {
                 fieldPosition.setEndIndex(result.length());
             }
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+            if (fieldPosition.getBeginIndex() < 0) {
+                fieldPosition.setBeginIndex(result.length());
+            }
+            fieldPosition.setEndIndex(result.length());
+        }
+        if (recordFractionDigits) {
+            ((UFieldPosition) fieldPosition).setFractionDigits(fractionalDigitsCount, fractionalDigits);
+        }
 
-            // [Spark/CDL] Add attribute information if necessary.
-            if (parseAttr && (decimalSeparatorAlwaysShown || fractionPresent)) {
+        // [Spark/CDL] Calcuate the end index of integer part and fractional
+        // part if they are not properly processed yet.
+        if (parseAttr) {
+            if (intEnd < 0) {
+                addAttribute(Field.INTEGER, intBegin, result.length());
+            }
+            if (fracBegin > 0) {
                 addAttribute(Field.FRACTION, fracBegin, result.length());
             }
         }
 
-        int suffixLen = appendAffix(result, isNegative, false, parseAttr);
-
-        addPadding(result, fieldPosition, prefixLen, suffixLen);
-        return result;
+        // The exponent is output using the pattern-specified minimum exponent
+        // digits. There is no maximum limit to the exponent digits, since truncating
+        // the exponent would result in an unacceptable inaccuracy.
+        result.append(symbols.getExponentSeparator());
+        // [Spark/CDL] For exponent symbol, add an attribute.
+        if (parseAttr) {
+            addAttribute(Field.EXPONENT_SYMBOL, result.length() -
+                         symbols.getExponentSeparator().length(), result.length());
+        }
+        // For zero values, we force the exponent to zero. We must do this here, and
+        // not earlier, because the value is used to determine integer digit count
+        // above.
+        if (digitList.isZero())
+            exponent = 0;
+
+        boolean negativeExponent = exponent < 0;
+        if (negativeExponent) {
+            exponent = -exponent;
+            result.append(symbols.getMinusSign());
+            // [Spark/CDL] If exponent has sign, then add an exponent sign
+            // attribute.
+            if (parseAttr) {
+                // Length of exponent sign is 1.
+                addAttribute(Field.EXPONENT_SIGN, result.length() - 1, result.length());
+            }
+        } else if (exponentSignAlwaysShown) {
+            result.append(symbols.getPlusSign());
+            // [Spark/CDL] Add an plus sign attribute.
+            if (parseAttr) {
+                // Length of exponent sign is 1.
+                int expSignBegin = result.length() - 1;
+                addAttribute(Field.EXPONENT_SIGN, expSignBegin, result.length());
+            }
+        }
+        int expBegin = result.length();
+        digitList.set(exponent);
+        {
+            int expDig = minExponentDigits;
+            if (useExponentialNotation && expDig < 1) {
+                expDig = 1;
+            }
+            for (i = digitList.decimalAt; i < expDig; ++i)
+                result.append(digits[0]);
+        }
+        for (i = 0; i < digitList.decimalAt; ++i) {
+            result.append((i < digitList.count) ? digits[digitList.getDigitValue(i)]
+                          : digits[0]);
+        }
+        // [Spark/CDL] Add attribute for exponent part.
+        if (parseAttr) {
+            addAttribute(Field.EXPONENT, expBegin, result.length());
+        }
     }
 
     private final void addPadding(StringBuffer result, FieldPosition fieldPosition, int prefixLen,
@@ -1638,8 +1838,9 @@ public class DecimalFormat extends NumberFormat {
      * <code>null</code> if the parse failed
      * @stable ICU 2.0
      */
+    @Override
     public Number parse(String text, ParsePosition parsePosition) {
-        return (Number) parse(text, parsePosition, false);
+        return (Number) parse(text, parsePosition, null);
     }
 
     /**
@@ -1649,15 +1850,17 @@ public class DecimalFormat extends NumberFormat {
      * code. This method will fail if this format is not a currency format, that is, if it
      * does not contain the currency pattern symbol (U+00A4) in its prefix or suffix.
      *
-     * @param text the string to parse
+     * @param text the text to parse
      * @param pos input-output position; on input, the position within text to match; must
      *  have 0 <= pos.getIndex() < text.length(); on output, the position after the last
      *  matched character. If the parse fails, the position in unchanged upon output.
      * @return a CurrencyAmount, or null upon failure
-     * @internal
+     * @stable ICU 49
      */
-    public CurrencyAmount parseCurrency(String text, ParsePosition pos) {
-        return (CurrencyAmount) parse(text, pos, true);
+    @Override
+    public CurrencyAmount parseCurrency(CharSequence text, ParsePosition pos) {
+        Currency[] currency = new Currency[1];
+        return (CurrencyAmount) parse(text.toString(), pos, currency);
     }
 
     /**
@@ -1668,11 +1871,11 @@ public class DecimalFormat extends NumberFormat {
      * match; must have 0 <= pos.getIndex() < text.length(); on output, the position after
      * the last matched character. If the parse fails, the position in unchanged upon
      * output.
-     * @param parseCurrency if true, a CurrencyAmount is parsed and returned; otherwise a
+     * @param currency if non-null, a CurrencyAmount is parsed and returned; otherwise a
      * Number is parsed and returned
      * @return a Number or CurrencyAmount or null
      */
-    private Object parse(String text, ParsePosition parsePosition, boolean parseCurrency) {
+    private Object parse(String text, ParsePosition parsePosition, Currency[] currency) {
         int backup;
         int i = backup = parsePosition.getIndex();
 
@@ -1698,15 +1901,14 @@ public class DecimalFormat extends NumberFormat {
         i = backup;
 
         boolean[] status = new boolean[STATUS_LENGTH];
-        Currency[] currency = parseCurrency ? new Currency[1] : null;
-        if (currencySignCount > 0) {
-            if (!parseForCurrency(text, parsePosition, parseCurrency, currency, status)) {
+        if (currencySignCount != CURRENCY_SIGN_COUNT_ZERO) {
+            if (!parseForCurrency(text, parsePosition, currency, status)) {
                 return null;
             }
         } else {
             if (!subparse(text, parsePosition, digitList, status, currency, negPrefixPattern,
                           negSuffixPattern, posPrefixPattern, posSuffixPattern,
-                          Currency.SYMBOL_NAME)) {
+                          false, Currency.SYMBOL_NAME)) {
                 parsePosition.setIndex(backup);
                 return null;
             }
@@ -1756,10 +1958,10 @@ public class DecimalFormat extends NumberFormat {
                             l = -l;
                         }
                     }
-                    n = new Long(l);
+                    n = Long.valueOf(l);
                 } else {
                     BigInteger big = digitList.getBigInteger(status[STATUS_POSITIVE]);
-                    n = (big.bitLength() < 64) ? (Number) new Long(big.longValue()) : (Number) big;
+                    n = (big.bitLength() < 64) ? (Number) Long.valueOf(big.longValue()) : (Number) big;
                 }
             }
             // Handle non-integral values or the case where parseBigDecimal is set
@@ -1773,10 +1975,10 @@ public class DecimalFormat extends NumberFormat {
         }
 
         // Assemble into CurrencyAmount if necessary
-        return parseCurrency ? (Object) new CurrencyAmount(n, currency[0]) : (Object) n;
+        return (currency != null) ? (Object) new CurrencyAmount(n, currency[0]) : (Object) n;
     }
 
-    private boolean parseForCurrency(String text, ParsePosition parsePosition, boolean parseCurrency,
+    private boolean parseForCurrency(String text, ParsePosition parsePosition,
             Currency[] currency, boolean[] status) {
         int origPos = parsePosition.getIndex();
         if (!isReadyForParsing) {
@@ -1804,11 +2006,11 @@ public class DecimalFormat extends NumberFormat {
         if (style == NumberFormat.PLURALCURRENCYSTYLE) {
             found = subparse(text, tmpPos, tmpDigitList, tmpStatus, currency,
                              negPrefixPattern, negSuffixPattern, posPrefixPattern, posSuffixPattern,
-                             Currency.LONG_NAME);
+                             true, Currency.LONG_NAME);
         } else {
             found = subparse(text, tmpPos, tmpDigitList, tmpStatus, currency,
                              negPrefixPattern, negSuffixPattern, posPrefixPattern, posSuffixPattern,
-                             Currency.SYMBOL_NAME);
+                             true, Currency.SYMBOL_NAME);
         }
         if (found) {
             if (tmpPos.getIndex() > maxPosIndex) {
@@ -1828,7 +2030,7 @@ public class DecimalFormat extends NumberFormat {
             boolean result = subparse(text, tmpPos, tmpDigitList, tmpStatus, currency,
                                       affix.getNegPrefix(), affix.getNegSuffix(),
                                       affix.getPosPrefix(), affix.getPosSuffix(),
-                                      affix.getPatternType());
+                                      true, affix.getPatternType());
             if (result) {
                 found = true;
                 if (tmpPos.getIndex() > maxPosIndex) {
@@ -1846,17 +2048,20 @@ public class DecimalFormat extends NumberFormat {
         // complexAffixCompare will not find match, since there is no ISO code matches
         // "\u00A4", and the parse stops at "\u00A4".  We will just use simple affix
         // comparison (look for exact match) to pass it.
+        //
+        // TODO: We should parse against simple affix first when
+        // output currency is not requested. After the complex currency
+        // parsing implementation was introduced, the default currency
+        // instance parsing slowed down because of the new code flow.
+        // I filed #10312 - Yoshito
         tmpStatus = new boolean[STATUS_LENGTH];
         tmpPos = new ParsePosition(origPos);
         tmpDigitList = new DigitList();
-        int savedCurrencySignCount = currencySignCount;
-        // set currencySignCount to 0 so that compareAffix function will fall to
-        // compareSimpleAffix path, not compareComplexAffix path.
-        currencySignCount = 0;
+
+        // Disable complex currency parsing and try it again.
         boolean result = subparse(text, tmpPos, tmpDigitList, tmpStatus, currency,
                                   negativePrefix, negativeSuffix, positivePrefix, positiveSuffix,
-                                  Currency.SYMBOL_NAME);
-        currencySignCount = savedCurrencySignCount;
+                                  false /* disable complex currency parsing */, Currency.SYMBOL_NAME);
         if (result) {
             if (tmpPos.getIndex() > maxPosIndex) {
                 maxPosIndex = tmpPos.getIndex();
@@ -1923,6 +2128,8 @@ public class DecimalFormat extends NumberFormat {
         formatPattern = savedFormatPattern;
     }
 
+    // currency formatting style options
+    private static final int CURRENCY_SIGN_COUNT_ZERO = 0;
     private static final int CURRENCY_SIGN_COUNT_IN_SYMBOL_FORMAT = 1;
     private static final int CURRENCY_SIGN_COUNT_IN_ISO_FORMAT = 2;
     private static final int CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT = 3;
@@ -2043,13 +2250,34 @@ public class DecimalFormat extends NumberFormat {
                 0xFF0C, 0xFF0C,
                 0xFF0E, 0xFF0E,
                 0xFF61, 0xFF61).freeze();
+    
+    private static final UnicodeSet minusSigns =
+        new UnicodeSet(
+                0x002D, 0x002D,
+                0x207B, 0x207B,
+                0x208B, 0x208B,
+                0x2212, 0x2212,
+                0x2796, 0x2796,
+                0xFE63, 0xFE63,
+                0xFF0D, 0xFF0D).freeze();
+    
+    private static final UnicodeSet plusSigns =
+            new UnicodeSet(
+                0x002B, 0x002B,
+                0x207A, 0x207A,
+                0x208A, 0x208A,
+                0x2795, 0x2795,
+                0xFB29, 0xFB29,
+                0xFE62, 0xFE62,
+                0xFF0B, 0xFF0B).freeze();
+    
 
     // When parsing a number with big exponential value, it requires to transform the
     // value into a string representation to construct BigInteger instance.  We want to
     // set the maximum size because it can easily trigger OutOfMemoryException.
-    // PARSE_MAX_EXPONENT is currently set to 1000, which is much bigger than MAX_VALUE of
-    // Double ( See the problem reported by ticket#5698
-    private static final int PARSE_MAX_EXPONENT = 1000;
+    // PARSE_MAX_EXPONENT is currently set to 1000 (See getParseMaxDigits()),
+    // which is much bigger than MAX_VALUE of Double ( See the problem reported by ticket#5698
+    private int PARSE_MAX_EXPONENT = 1000;
 
     /**
      * Parses the given text into a number. The text is parsed beginning at parsePosition,
@@ -2068,12 +2296,13 @@ public class DecimalFormat extends NumberFormat {
      * @param negSuffix negative suffix pattern
      * @param posPrefix positive prefix pattern
      * @param negSuffix negative suffix pattern
+     * @param complexCurrencyParsing whether it is complex currency parsing or not.
      * @param type type of currency to parse against, LONG_NAME only or not.
      */
     private final boolean subparse(
         String text, ParsePosition parsePosition, DigitList digits,
         boolean status[], Currency currency[], String negPrefix, String negSuffix, String posPrefix,
-        String posSuffix, int type) {
+        String posSuffix, boolean parseComplexCurrency, int type) {
 
         int position = parsePosition.getIndex();
         int oldStart = parsePosition.getIndex();
@@ -2084,8 +2313,8 @@ public class DecimalFormat extends NumberFormat {
         }
 
         // Match positive and negative prefixes; prefer longest match.
-        int posMatch = compareAffix(text, position, false, true, posPrefix, type, currency);
-        int negMatch = compareAffix(text, position, true, true, negPrefix, type, currency);
+        int posMatch = compareAffix(text, position, false, true, posPrefix, parseComplexCurrency, type, currency);
+        int negMatch = compareAffix(text, position, true, true, negPrefix, parseComplexCurrency, type, currency);
         if (posMatch >= 0 && negMatch >= 0) {
             if (posMatch > negMatch) {
                 negMatch = -1;
@@ -2122,9 +2351,10 @@ public class DecimalFormat extends NumberFormat {
 
             digits.decimalAt = digits.count = 0;
             char [] digitSymbols = symbols.getDigitsLocal();
-            char decimal = currencySignCount > 0 ? symbols.getMonetaryDecimalSeparator() : symbols
-                    .getDecimalSeparator();
-            char grouping = symbols.getGroupingSeparator();
+            char decimal = (currencySignCount == CURRENCY_SIGN_COUNT_ZERO) ?
+                    symbols.getDecimalSeparator() : symbols.getMonetaryDecimalSeparator();
+            char grouping = (currencySignCount == CURRENCY_SIGN_COUNT_ZERO) ?
+                    symbols.getGroupingSeparator() : symbols.getMonetaryGroupingSeparator();
 
             String exponentSep = symbols.getExponentSeparator();
             boolean sawDecimal = false;
@@ -2138,6 +2368,7 @@ public class DecimalFormat extends NumberFormat {
             boolean strictParse = isParseStrict();
             boolean strictFail = false; // did we exit with a strict parse failure?
             int lastGroup = -1; // where did we last see a grouping separator?
+            int digitStart = position; // where did the digit start?
             int gs2 = groupingSize2 == 0 ? groupingSize : groupingSize2;
 
             // equivalent grouping and decimal support
@@ -2178,7 +2409,7 @@ public class DecimalFormat extends NumberFormat {
                             break;
                     }
                 }
-                    
+
 
 
                 if (digit == 0) {
@@ -2188,8 +2419,8 @@ public class DecimalFormat extends NumberFormat {
                         // group. If there was a group separator before that, the group
                         // must == the secondary group length, else it can be <= the the
                         // secondary group length.
-                        if ((lastGroup != -1 && countCodePoints(text,lastGroup,backup) - 1 != gs2)
-                                || (lastGroup == -1 && countCodePoints(text,oldStart,position) - 1 > gs2)) {
+                        if ((lastGroup != -1 && countCodePoints(text, lastGroup, backup) - 1 != gs2)
+                                || (lastGroup == -1 && countCodePoints(text, digitStart, position) - 1 > gs2)) {
                             strictFail = true;
                             break;
                         }
@@ -2217,8 +2448,8 @@ public class DecimalFormat extends NumberFormat {
                 {
                     if (strictParse) {
                         if (backup != -1) {
-                            if ((lastGroup != -1 && countCodePoints(text,lastGroup,backup) - 1 != gs2)
-                                    || (lastGroup == -1 && countCodePoints(text,oldStart,position) - 1 > gs2)) {
+                            if ((lastGroup != -1 && countCodePoints(text, lastGroup, backup) - 1 != gs2)
+                                    || (lastGroup == -1 && countCodePoints(text, digitStart, position) - 1 > gs2)) {
                                 strictFail = true;
                                 break;
                             }
@@ -2393,9 +2624,9 @@ public class DecimalFormat extends NumberFormat {
 
             // Adjust for exponent, if any
             exponent += digits.decimalAt;
-            if (exponent < -PARSE_MAX_EXPONENT) {
+            if (exponent < -getParseMaxDigits()) {
                 status[STATUS_UNDERFLOW] = true;
-            } else if (exponent > PARSE_MAX_EXPONENT) {
+            } else if (exponent > getParseMaxDigits()) {
                 status[STATUS_INFINITE] = true;
             } else {
                 digits.decimalAt = (int) exponent;
@@ -2418,10 +2649,10 @@ public class DecimalFormat extends NumberFormat {
 
         // Match positive and negative suffixes; prefer longest match.
         if (posMatch >= 0) {
-            posMatch = compareAffix(text, position, false, false, posSuffix, type, currency);
+            posMatch = compareAffix(text, position, false, false, posSuffix, parseComplexCurrency, type, currency);
         }
         if (negMatch >= 0) {
-            negMatch = compareAffix(text, position, true, false, negSuffix, type, currency);
+            negMatch = compareAffix(text, position, true, false, negSuffix, parseComplexCurrency, type, currency);
         }
         if (posMatch >= 0 && negMatch >= 0) {
             if (posMatch > negMatch) {
@@ -2455,7 +2686,7 @@ public class DecimalFormat extends NumberFormat {
         return true;
     }
 
-    // Utility method used to count the number of codepoints 
+    // Utility method used to count the number of codepoints
     private int countCodePoints(String str,int start, int end) {
         int count = 0;
         int index = start;
@@ -2509,6 +2740,7 @@ public class DecimalFormat extends NumberFormat {
      * @param isNegative
      * @param isPrefix
      * @param affixPat affix pattern used for currency affix comparison
+     * @param copmplexCurrencyParsing whether it is currency parsing or not
      * @param type compare against currency type, LONG_NAME only or not.
      * @param currency return value for parsed currency, for generic currency parsing
      * mode, or null for normal parsing.  In generic currency parsing mode, any currency
@@ -2516,8 +2748,8 @@ public class DecimalFormat extends NumberFormat {
      * @return length of input that matches, or -1 if match failure
      */
     private int compareAffix(String text, int pos, boolean isNegative, boolean isPrefix,
-                             String affixPat, int type, Currency[] currency) {
-        if (currency != null || currencyChoice != null || currencySignCount > 0) {
+                             String affixPat, boolean complexCurrencyParsing, int type, Currency[] currency) {
+        if (currency != null || currencyChoice != null || (currencySignCount != CURRENCY_SIGN_COUNT_ZERO && complexCurrencyParsing)) {
             return compareComplexAffix(affixPat, text, pos, type, currency);
         }
         if (isPrefix) {
@@ -2528,6 +2760,43 @@ public class DecimalFormat extends NumberFormat {
 
     }
 
+    /**
+     * Check for bidi marks: LRM, RLM, ALM
+     */
+    private static boolean isBidiMark(int c) {
+        return (c==0x200E || c==0x200F || c==0x061C);
+    }
+
+    /**
+     * Remove bidi marks from affix
+     */
+    private static final int TRIM_BUFLEN = 32;
+    private static String trimMarksFromAffix(String affix) { 
+        boolean hasBidiMark = false; 
+        int idx = 0; 
+        for (; idx < affix.length(); idx++) { 
+            if (isBidiMark(affix.charAt(idx))) { 
+                hasBidiMark = true; 
+                break; 
+            } 
+        } 
+        if (!hasBidiMark) { 
+            return affix; 
+        } 
+
+        StringBuilder buf = new StringBuilder(); 
+        buf.append(affix, 0, idx); 
+        idx++;  // skip the first Bidi mark 
+        for (; idx < affix.length(); idx++) { 
+            char c = affix.charAt(idx); 
+            if (!isBidiMark(c)) { 
+                buf.append(c); 
+            } 
+        } 
+
+        return buf.toString(); 
+    } 
+
     /**
      * Return the length matched by the given affix, or -1 if none. Runs of white space in
      * the affix, match runs of white space in the input. Pattern white space and input
@@ -2540,8 +2809,12 @@ public class DecimalFormat extends NumberFormat {
      */
     private static int compareSimpleAffix(String affix, String input, int pos) {
         int start = pos;
-        for (int i = 0; i < affix.length();) {
-            int c = UTF16.charAt(affix, i);
+        // Affixes here might consist of sign, currency symbol and related spacing, etc.
+        // For more efficiency we should keep lazily-created trimmed affixes around in
+        // instance variables instead of trimming each time they are used (the next step).
+        String trimmedAffix = (affix.length() > 1)? trimMarksFromAffix(affix): affix;
+        for (int i = 0; i < trimmedAffix.length();) {
+            int c = UTF16.charAt(trimmedAffix, i);
             int len = UTF16.getCharCount(c);
             if (PatternProps.isWhiteSpace(c)) {
                 // We may have a pattern like: \u200F and input text like: \u200F Note
@@ -2549,22 +2822,29 @@ public class DecimalFormat extends NumberFormat {
                 // UWhiteSpace. So we have to first do a direct match of the run of RULE
                 // whitespace in the pattern, then match any extra characters.
                 boolean literalMatch = false;
-                while (pos < input.length() && UTF16.charAt(input, pos) == c) {
-                    literalMatch = true;
-                    i += len;
-                    pos += len;
-                    if (i == affix.length()) {
-                        break;
-                    }
-                    c = UTF16.charAt(affix, i);
-                    len = UTF16.getCharCount(c);
-                    if (!PatternProps.isWhiteSpace(c)) {
+                while (pos < input.length()) {
+                    int ic = UTF16.charAt(input, pos);
+                    if (ic == c) {
+                        literalMatch = true;
+                        i += len;
+                        pos += len;
+                        if (i == trimmedAffix.length()) {
+                            break;
+                        }
+                        c = UTF16.charAt(trimmedAffix, i);
+                        len = UTF16.getCharCount(c);
+                        if (!PatternProps.isWhiteSpace(c)) {
+                            break;
+                        }
+                    } else if (isBidiMark(ic)) {
+                        pos++; // just skip over this input text
+                    } else {
                         break;
                     }
                 }
 
-                // Advance over run in affix
-                i = skipPatternWhiteSpace(affix, i);
+                // Advance over run in trimmedAffix
+                i = skipPatternWhiteSpace(trimmedAffix, i);
 
                 // Advance over run in input text. Must see at least one white space char
                 // in input, unless we've already matched some characters literally.
@@ -2575,13 +2855,23 @@ public class DecimalFormat extends NumberFormat {
                 }
                 // If we skip UWhiteSpace in the input text, we need to skip it in the
                 // pattern.  Otherwise, the previous lines may have skipped over text
-                // (such as U+00A0) that is also in the affix.
-                i = skipUWhiteSpace(affix, i);
+                // (such as U+00A0) that is also in the trimmedAffix.
+                i = skipUWhiteSpace(trimmedAffix, i);
             } else {
-                if (pos < input.length() && UTF16.charAt(input, pos) == c) {
-                    i += len;
-                    pos += len;
-                } else {
+                boolean match = false;
+                while (pos < input.length()) {
+                    int ic = UTF16.charAt(input, pos);
+                    if (!match && equalWithSignCompatibility(ic, c)) {
+                        i += len;
+                        pos += len;
+                        match = true;
+                    } else if (isBidiMark(ic)) {
+                        pos++; // just skip over this input text
+                    } else {
+                        break;
+                    }
+                }
+                if (!match) {
                     return -1;
                 }
             }
@@ -2589,6 +2879,12 @@ public class DecimalFormat extends NumberFormat {
         return pos - start;
     }
 
+    private static boolean equalWithSignCompatibility(int lhs, int rhs) {
+        return lhs == rhs
+                || (minusSigns.contains(lhs) && minusSigns.contains(rhs))
+                || (plusSigns.contains(lhs) && plusSigns.contains(rhs));
+    }
+
     /**
      * Skips over a run of zero or more Pattern_White_Space characters at pos in text.
      */
@@ -2617,7 +2913,21 @@ public class DecimalFormat extends NumberFormat {
         return pos;
     }
 
-    /**
+     /**
+     * Skips over a run of zero or more bidi marks at pos in text.
+     */
+    private static int skipBidiMarks(String text, int pos) {
+        while (pos < text.length()) {
+            int c = UTF16.charAt(text, pos);
+            if (!isBidiMark(c)) {
+                break;
+            }
+            pos += UTF16.getCharCount(c);
+        }
+        return pos;
+    }
+
+   /**
      * Returns the length matched by the given affix, or -1 if none.
      *
      * @param affixPat pattern string
@@ -2734,9 +3044,10 @@ public class DecimalFormat extends NumberFormat {
      * white space in text.
      */
     static final int match(String text, int pos, int ch) {
-        if (pos >= text.length()) {
+        if (pos < 0 || pos >= text.length()) {
             return -1;
         }
+        pos = skipBidiMarks(text, pos);
         if (PatternProps.isWhiteSpace(ch)) {
             // Advance over run of white space in input text
             // Must see at least one white space char in input
@@ -2747,7 +3058,11 @@ public class DecimalFormat extends NumberFormat {
             }
             return pos;
         }
-        return (pos >= 0 && UTF16.charAt(text, pos) == ch) ? (pos + UTF16.getCharCount(ch)) : -1;
+        if (pos >= text.length() || UTF16.charAt(text, pos) != ch) {
+            return -1;
+        }
+        pos = skipBidiMarks(text, pos + UTF16.getCharCount(ch));
+        return pos;
     }
 
     /**
@@ -2954,31 +3269,25 @@ public class DecimalFormat extends NumberFormat {
     /**
      * {@icu} Returns the rounding increment.
      *
-     * @return A positive rounding increment, or <code>null</code> if rounding is not in
-     * effect.
+     * @return A positive rounding increment, or <code>null</code> if a custom rounding
+     * increment is not in effect.
      * @see #setRoundingIncrement
      * @see #getRoundingMode
      * @see #setRoundingMode
      * @stable ICU 2.0
      */
-//#if defined(ECLIPSE)
-//##    public BigDecimal getRoundingIncrement() {
-//##        return roundingIncrementICU;
-//##    }
-//#else
     public java.math.BigDecimal getRoundingIncrement() {
         if (roundingIncrementICU == null)
             return null;
         return roundingIncrementICU.toBigDecimal();
     }
-//#endif
 
     /**
-     * {@icu} Sets the rounding increment. This method also controls whether rounding is
-     * enabled.
+     * {@icu} Sets the rounding increment. In the absence of a rounding increment, numbers
+     * will be rounded to the number of digits displayed.
      *
      * @param newValue A positive rounding increment, or <code>null</code> or
-     * <code>BigDecimal(0.0)</code> to disable rounding.
+     * <code>BigDecimal(0.0)</code> to use the default rounding increment.
      * @throws IllegalArgumentException if <code>newValue</code> is < 0.0
      * @see #getRoundingIncrement
      * @see #getRoundingMode
@@ -2994,11 +3303,11 @@ public class DecimalFormat extends NumberFormat {
     }
 
     /**
-     * {@icu} Sets the rounding increment. This method also controls whether rounding is
-     * enabled.
+     * {@icu} Sets the rounding increment. In the absence of a rounding increment, numbers
+     * will be rounded to the number of digits displayed.
      *
      * @param newValue A positive rounding increment, or <code>null</code> or
-     * <code>BigDecimal(0.0)</code> to disable rounding.
+     * <code>BigDecimal(0.0)</code> to use the default rounding increment.
      * @throws IllegalArgumentException if <code>newValue</code> is < 0.0
      * @see #getRoundingIncrement
      * @see #getRoundingMode
@@ -3015,14 +3324,15 @@ public class DecimalFormat extends NumberFormat {
         } else {
             setInternalRoundingIncrement(newValue);
         }
-        setRoundingDouble();
+        resetActualRounding();
     }
 
     /**
-     * {@icu} Sets the rounding increment. This method also controls whether rounding is
-     * enabled.
+     * {@icu} Sets the rounding increment. In the absence of a rounding increment, numbers
+     * will be rounded to the number of digits displayed.
      *
-     * @param newValue A positive rounding increment, or 0.0 to disable rounding.
+     * @param newValue A positive rounding increment, or 0.0 to use the default
+     * rounding increment.
      * @throws IllegalArgumentException if <code>newValue</code> is < 0.0
      * @see #getRoundingIncrement
      * @see #getRoundingMode
@@ -3033,29 +3343,16 @@ public class DecimalFormat extends NumberFormat {
         if (newValue < 0.0) {
             throw new IllegalArgumentException("Illegal rounding increment");
         }
-        roundingDouble = newValue;
-        roundingDoubleReciprocal = 0.0d;
         if (newValue == 0.0d) {
-            setRoundingIncrement((BigDecimal) null);
+            setInternalRoundingIncrement((BigDecimal) null);
         } else {
-            roundingDouble = newValue;
-            if (roundingDouble < 1.0d) {
-                double rawRoundedReciprocal = 1.0d / roundingDouble;
-                setRoundingDoubleReciprocal(rawRoundedReciprocal);
-            }
-            setInternalRoundingIncrement(new BigDecimal(newValue));
+            // Should use BigDecimal#valueOf(double) instead of constructor
+            // to avoid the double precision problem.
+            setInternalRoundingIncrement(BigDecimal.valueOf(newValue));
         }
+        resetActualRounding();
     }
 
-    private void setRoundingDoubleReciprocal(double rawRoundedReciprocal) {
-        roundingDoubleReciprocal = Math.rint(rawRoundedReciprocal);
-        if (Math.abs(rawRoundedReciprocal - roundingDoubleReciprocal) > roundingIncrementEpsilon) {
-            roundingDoubleReciprocal = 0.0d;
-        }
-    }
-
-    static final double roundingIncrementEpsilon = 0.000000001;
-
     /**
      * Returns the rounding mode.
      *
@@ -3067,6 +3364,7 @@ public class DecimalFormat extends NumberFormat {
      * @see java.math.BigDecimal
      * @stable ICU 2.0
      */
+    @Override
     public int getRoundingMode() {
         return roundingMode;
     }
@@ -3084,16 +3382,14 @@ public class DecimalFormat extends NumberFormat {
      * @see java.math.BigDecimal
      * @stable ICU 2.0
      */
+    @Override
     public void setRoundingMode(int roundingMode) {
         if (roundingMode < BigDecimal.ROUND_UP || roundingMode > BigDecimal.ROUND_UNNECESSARY) {
             throw new IllegalArgumentException("Invalid rounding mode: " + roundingMode);
         }
 
         this.roundingMode = roundingMode;
-
-        if (getRoundingIncrement() == null) {
-            setRoundingIncrement(Math.pow(10.0, (double) -getMaximumFractionDigits()));
-        }
+        resetActualRounding();
     }
 
     /**
@@ -3499,6 +3795,7 @@ public class DecimalFormat extends NumberFormat {
      * Overrides clone.
      * @stable ICU 2.0
      */
+    @Override
     public Object clone() {
         try {
             DecimalFormat other = (DecimalFormat) super.clone();
@@ -3507,6 +3804,7 @@ public class DecimalFormat extends NumberFormat {
             if (currencyPluralInfo != null) {
                 other.currencyPluralInfo = (CurrencyPluralInfo) currencyPluralInfo.clone();
             }
+            other.attributes = new ArrayList<FieldPosition>(); // #9240
 
             // TODO: We need to figure out whether we share a single copy of DigitList by
             // multiple cloned copies.  format/subformat are designed to use a single
@@ -3521,6 +3819,7 @@ public class DecimalFormat extends NumberFormat {
      * Overrides equals.
      * @stable ICU 2.0
      */
+    @Override
     public boolean equals(Object obj) {
         if (obj == null)
             return false;
@@ -3601,6 +3900,7 @@ public class DecimalFormat extends NumberFormat {
      * Overrides hashCode.
      * @stable ICU 2.0
      */
+    @Override
     public int hashCode() {
         return super.hashCode() * 37 + positivePrefix.hashCode();
         // just enough fields for a reasonable distribution
@@ -3808,8 +4108,9 @@ public class DecimalFormat extends NumberFormat {
                 c = symbols.getPerMill();
                 break;
             case PATTERN_MINUS:
-                c = symbols.getMinusSign();
-                break;
+                String minusString = symbols.getMinusString();
+                buffer.append(minusString);
+                continue;
             }
             buffer.append(c);
         }
@@ -3893,23 +4194,31 @@ public class DecimalFormat extends NumberFormat {
      *
      * @stable ICU 3.6
      */
+    @Override
     public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
+      return formatToCharacterIterator(obj, NULL_UNIT);
+    }
+
+    AttributedCharacterIterator formatToCharacterIterator(Object obj, Unit unit) {
         if (!(obj instanceof Number))
             throw new IllegalArgumentException();
         Number number = (Number) obj;
-        StringBuffer text = null;
+        StringBuffer text = new StringBuffer();
+        unit.writePrefix(text);
         attributes.clear();
         if (obj instanceof BigInteger) {
-            text = format((BigInteger) number, new StringBuffer(), new FieldPosition(0), true);
+            format((BigInteger) number, text, new FieldPosition(0), true);
         } else if (obj instanceof java.math.BigDecimal) {
-            text = format((java.math.BigDecimal) number, new StringBuffer(), new FieldPosition(0)
+            format((java.math.BigDecimal) number, text, new FieldPosition(0)
                           , true);
         } else if (obj instanceof Double) {
-            text = format(number.doubleValue(), new StringBuffer(), new FieldPosition(0), true);
+            format(number.doubleValue(), text, new FieldPosition(0), true);
         } else if (obj instanceof Integer || obj instanceof Long) {
-            text = format(number.longValue(), new StringBuffer(), new FieldPosition(0), true);
+            format(number.longValue(), text, new FieldPosition(0), true);
+        } else {
+            throw new IllegalArgumentException();
         }
-
+        unit.writeSuffix(text);
         AttributedString as = new AttributedString(text.toString());
 
         // add NumberFormat field attributes to the AttributedString
@@ -4620,7 +4929,7 @@ public class DecimalFormat extends NumberFormat {
                     // [Richard/GCL]
                     setMaximumIntegerDigits(useExponentialNotation ? digitLeftCount + minInt :
                                             DOUBLE_INTEGER_DIGITS);
-                    setMaximumFractionDigits(decimalPos >= 0 ?
+                    _setMaximumFractionDigits(decimalPos >= 0 ?
                                              (digitTotalCount - decimalPos) : 0);
                     setMinimumFractionDigits(decimalPos >= 0 ?
                                              (digitLeftCount + zeroDigitCount - decimalPos) : 0);
@@ -4646,7 +4955,6 @@ public class DecimalFormat extends NumberFormat {
                     if (scale < 0) {
                         roundingIncrementICU = roundingIncrementICU.movePointRight(-scale);
                     }
-                    setRoundingDouble();
                     roundingMode = BigDecimal.ROUND_HALF_EVEN;
                 } else {
                     setRoundingIncrement((BigDecimal) null);
@@ -4670,7 +4978,7 @@ public class DecimalFormat extends NumberFormat {
             setMinimumIntegerDigits(0);
             setMaximumIntegerDigits(DOUBLE_INTEGER_DIGITS);
             setMinimumFractionDigits(0);
-            setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS);
+            _setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS);
         }
 
         // If there was no negative pattern, or if the negative pattern is identical to
@@ -4690,7 +4998,7 @@ public class DecimalFormat extends NumberFormat {
         formatPattern = pattern;
 
         // special handlings for currency instance
-        if (currencySignCount > 0) {
+        if (currencySignCount != CURRENCY_SIGN_COUNT_ZERO) {
             // reset rounding increment and max/min fractional digits
             // by the currency
             Currency theCurrency = getCurrency();
@@ -4698,7 +5006,7 @@ public class DecimalFormat extends NumberFormat {
                 setRoundingIncrement(theCurrency.getRoundingIncrement());
                 int d = theCurrency.getDefaultFractionDigits();
                 setMinimumFractionDigits(d);
-                setMaximumFractionDigits(d);
+                _setMaximumFractionDigits(d);
             }
 
             // initialize currencyPluralInfo if needed
@@ -4707,22 +5015,9 @@ public class DecimalFormat extends NumberFormat {
                 currencyPluralInfo = new CurrencyPluralInfo(symbols.getULocale());
             }
         }
+        resetActualRounding();
     }
 
-    /**
-     * Centralizes the setting of the roundingDouble and roundingDoubleReciprocal.
-     */
-    private void setRoundingDouble() {
-        if (roundingIncrementICU == null) {
-            roundingDouble = 0.0d;
-            roundingDoubleReciprocal = 0.0d;
-        } else {
-            roundingDouble = roundingIncrementICU.doubleValue();
-            setRoundingDoubleReciprocal(
-                BigDecimal.ONE.divide(roundingIncrementICU, BigDecimal.ROUND_HALF_EVEN)
-                    .doubleValue());
-        }
-    }
 
     private void patternError(String msg, String pattern) {
         throw new IllegalArgumentException(msg + " in pattern \"" + pattern + '"');
@@ -4739,6 +5034,7 @@ public class DecimalFormat extends NumberFormat {
      * @see NumberFormat#setMaximumIntegerDigits
      * @stable ICU 2.0
      */
+    @Override
     public void setMaximumIntegerDigits(int newValue) {
         super.setMaximumIntegerDigits(Math.min(newValue, DOUBLE_INTEGER_DIGITS));
     }
@@ -4750,6 +5046,7 @@ public class DecimalFormat extends NumberFormat {
      * @see NumberFormat#setMinimumIntegerDigits
      * @stable ICU 2.0
      */
+    @Override
     public void setMinimumIntegerDigits(int newValue) {
         super.setMinimumIntegerDigits(Math.min(newValue, DOUBLE_INTEGER_DIGITS));
     }
@@ -4781,9 +5078,9 @@ public class DecimalFormat extends NumberFormat {
     /**
      * {@icu} Sets the minimum number of significant digits that will be displayed. If
      * <code>min</code> is less than one then it is set to one. If the maximum significant
-     * digits count is less than <code>min</code>, then it is set to
-     * <code>min</code>. This value has no effect unless {@link #areSignificantDigitsUsed()}
-     * returns true.
+     * digits count is less than <code>min</code>, then it is set to <code>min</code>. 
+     * This function also enables the use of significant digits by this formatter - 
+     * {@link #areSignificantDigitsUsed()} will return true.
      *
      * @param min the fewest significant digits to be shown
      * @stable ICU 3.0
@@ -4796,14 +5093,15 @@ public class DecimalFormat extends NumberFormat {
         int max = Math.max(maxSignificantDigits, min);
         minSignificantDigits = min;
         maxSignificantDigits = max;
+        setSignificantDigitsUsed(true);
     }
 
     /**
      * {@icu} Sets the maximum number of significant digits that will be displayed. If
      * <code>max</code> is less than one then it is set to one. If the minimum significant
-     * digits count is greater than <code>max</code>, then it is set to
-     * <code>max</code>. This value has no effect unless {@link #areSignificantDigitsUsed()}
-     * returns true.
+     * digits count is greater than <code>max</code>, then it is set to <code>max</code>.
+     * This function also enables the use of significant digits by this formatter - 
+     * {@link #areSignificantDigitsUsed()} will return true.
      *
      * @param max the most significant digits to be shown
      * @stable ICU 3.0
@@ -4816,6 +5114,7 @@ public class DecimalFormat extends NumberFormat {
         int min = Math.min(minSignificantDigits, max);
         minSignificantDigits = min;
         maxSignificantDigits = max;
+        setSignificantDigitsUsed(true);
     }
 
     /**
@@ -4850,6 +5149,7 @@ public class DecimalFormat extends NumberFormat {
      * @param theCurrency new currency object to use. Must not be null.
      * @stable ICU 2.2
      */
+    @Override
     public void setCurrency(Currency theCurrency) {
         // If we are a currency format, then modify our affixes to
         // encode the currency symbol for the given currency in our
@@ -4865,7 +5165,7 @@ public class DecimalFormat extends NumberFormat {
             symbols.setCurrencySymbol(s);
         }
 
-        if (currencySignCount > 0) {
+        if (currencySignCount != CURRENCY_SIGN_COUNT_ZERO) {
             if (theCurrency != null) {
                 setRoundingIncrement(theCurrency.getRoundingIncrement());
                 int d = theCurrency.getDefaultFractionDigits();
@@ -4887,6 +5187,8 @@ public class DecimalFormat extends NumberFormat {
      * @internal
      * @deprecated This API is ICU internal only.
      */
+    @Deprecated
+    @Override
     protected Currency getEffectiveCurrency() {
         Currency c = getCurrency();
         if (c == null) {
@@ -4902,7 +5204,17 @@ public class DecimalFormat extends NumberFormat {
      * @see NumberFormat#setMaximumFractionDigits
      * @stable ICU 2.0
      */
+    @Override
     public void setMaximumFractionDigits(int newValue) {
+        _setMaximumFractionDigits(newValue);
+        resetActualRounding();
+    }
+
+    /*
+     * Internal method for DecimalFormat, setting maximum fractional digits
+     * without triggering actual rounding recalculated.
+     */
+    private void _setMaximumFractionDigits(int newValue) {
         super.setMaximumFractionDigits(Math.min(newValue, DOUBLE_FRACTION_DIGITS));
     }
 
@@ -4913,6 +5225,7 @@ public class DecimalFormat extends NumberFormat {
      * @see NumberFormat#setMinimumFractionDigits
      * @stable ICU 2.0
      */
+    @Override
     public void setMinimumFractionDigits(int newValue) {
         super.setMinimumFractionDigits(Math.min(newValue, DOUBLE_FRACTION_DIGITS));
     }
@@ -4938,6 +5251,31 @@ public class DecimalFormat extends NumberFormat {
     public boolean isParseBigDecimal() {
         return parseBigDecimal;
     }
+    
+    /**
+    * Set the maximum number of exponent digits when parsing a number. 
+    * If the limit is set too high, an OutOfMemoryException may be triggered.
+    * The default value is 1000.
+    * @param newValue the new limit
+    * @draft ICU 51
+    * @provisional This API might change or be removed in a future release.
+    */
+    public void setParseMaxDigits(int newValue) {
+        if (newValue > 0) {
+            PARSE_MAX_EXPONENT = newValue;
+        }
+    }
+    
+    /**
+    * Get the current maximum number of exponent digits when parsing a
+    * number.
+    *
+    * @draft ICU 51
+    * @provisional This API might change or be removed in a future release.
+    */
+    public int getParseMaxDigits() {
+        return PARSE_MAX_EXPONENT;
+    }
 
     private void writeObject(ObjectOutputStream stream) throws IOException {
         // Ticket#6449 Format.Field instances are not serializable. When
@@ -4980,12 +5318,11 @@ public class DecimalFormat extends NumberFormat {
             setMaximumIntegerDigits(DOUBLE_INTEGER_DIGITS);
         }
         if (getMaximumFractionDigits() > DOUBLE_FRACTION_DIGITS) {
-            setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS);
+            _setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS);
         }
         if (serialVersionOnStream < 2) {
             exponentSignAlwaysShown = false;
             setInternalRoundingIncrement(null);
-            setRoundingDouble();
             roundingMode = BigDecimal.ROUND_HALF_EVEN;
             formatWidth = 0;
             pad = ' ';
@@ -5005,8 +5342,8 @@ public class DecimalFormat extends NumberFormat {
 
         if (roundingIncrement != null) {
             setInternalRoundingIncrement(new BigDecimal(roundingIncrement));
-            setRoundingDouble();
         }
+        resetActualRounding();
     }
 
     private void setInternalRoundingIncrement(BigDecimal value) {
@@ -5242,19 +5579,6 @@ public class DecimalFormat extends NumberFormat {
      */
     private transient BigDecimal roundingIncrementICU = null;
 
-    /**
-     * The rounding increment as a double. If this value is <= 0, then no rounding is
-     * done. This value is <code>roundingIncrementICU.doubleValue()</code>. Default value
-     * 0.0.
-     */
-    private transient double roundingDouble = 0.0;
-
-    /**
-     * If the roundingDouble is the reciprocal of an integer (the most common case!), this
-     * is set to be that integer.  Otherwise it is 0.0.
-     */
-    private transient double roundingDoubleReciprocal = 0.0;
-
     /**
      * The rounding mode. This value controls any rounding operations which occur when
      * applying a rounding increment or when reducing the number of fraction digits to
@@ -5493,7 +5817,7 @@ public class DecimalFormat extends NumberFormat {
      * -- plural long name, such as "US Dollar" for "1.00 US Dollar", or "US Dollars" for
      * "3.00 US Dollars".
      */
-    private int currencySignCount = 0;
+    private int currencySignCount = CURRENCY_SIGN_COUNT_ZERO;
 
     /**
      * For parsing purposes, we need to remember all prefix patterns and suffix patterns
@@ -5511,7 +5835,7 @@ public class DecimalFormat extends NumberFormat {
         private String posPrefixPatternForCurrency = null;
         // positive suffix pattern
         private String posSuffixPatternForCurrency = null;
-        private int patternType;
+        private final int patternType;
 
         public AffixForCurrency(String negPrefix, String negSuffix, String posPrefix,
                                 String posSuffix, int type) {
@@ -5554,6 +5878,134 @@ public class DecimalFormat extends NumberFormat {
 
     // Information needed for DecimalFormat to format/parse currency plural.
     private CurrencyPluralInfo currencyPluralInfo = null;
+
+    /**
+     * Unit is an immutable class for the textual representation of a unit, in
+     * particular its prefix and suffix.
+     *
+     * @author rocketman
+     *
+     */
+    static class Unit {
+        private final String prefix;
+        private final String suffix;
+
+        public Unit(String prefix, String suffix) {
+            this.prefix = prefix;
+            this.suffix = suffix;
+        }
+
+        public void writeSuffix(StringBuffer toAppendTo) {
+            toAppendTo.append(suffix);
+        }
+
+        public void writePrefix(StringBuffer toAppendTo) {
+            toAppendTo.append(prefix);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof Unit)) {
+                return false;
+            }
+            Unit other = (Unit) obj;
+            return prefix.equals(other.prefix) && suffix.equals(other.suffix);
+        }
+        @Override
+        public String toString() {
+            return prefix + "/" + suffix;
+        }
+    }
+
+    static final Unit NULL_UNIT = new Unit("", "");
+
+    // Note about rounding implementation
+    //
+    // The original design intended to skip rounding operation when roundingIncrement is not
+    // set. However, rounding may need to occur when fractional digits exceed the width of
+    // fractional part of pattern.
+    //
+    // DigitList class has built-in rounding mechanism, using ROUND_HALF_EVEN. This implementation
+    // forces non-null roundingIncrement if the setting is other than ROUND_HALF_EVEN, otherwise,
+    // when rounding occurs in DigitList by pattern's fractional digits' width, the result
+    // does not match the rounding mode.
+    //
+    // Ideally, all rounding operation should be done in one place like ICU4C trunk does
+    // (ICU4C rounding implementation was rewritten recently). This is intrim implemetation
+    // to fix various issues. In the future, we should entire implementation of rounding
+    // in this class, like ICU4C did.
+    //
+    // Once we fully implement rounding logic in DigitList, then following fields and methods
+    // should be gone.
+
+    private transient BigDecimal actualRoundingIncrementICU = null;
+    private transient java.math.BigDecimal actualRoundingIncrement = null;
+
+    /*
+     * The actual rounding increment as a double.
+     */
+    private transient double roundingDouble = 0.0;
+
+    /*
+     * If the roundingDouble is the reciprocal of an integer (the most common case!), this
+     * is set to be that integer.  Otherwise it is 0.0.
+     */
+    private transient double roundingDoubleReciprocal = 0.0;
+
+    /*
+     * Set roundingDouble, roundingDoubleReciprocal and actualRoundingIncrement
+     * based on rounding mode and width of fractional digits. Whenever setting affecting
+     * rounding mode, rounding increment and maximum width of fractional digits, then
+     * this method must be called.
+     * 
+     * roundingIncrementICU is the field storing the custom rounding increment value,
+     * while actual rounding increment could be larger.
+     */
+    private void resetActualRounding() {
+        if (roundingIncrementICU != null) {
+            BigDecimal byWidth = getMaximumFractionDigits() > 0 ?
+                    BigDecimal.ONE.movePointLeft(getMaximumFractionDigits()) : BigDecimal.ONE;
+            if (roundingIncrementICU.compareTo(byWidth) >= 0) {
+                actualRoundingIncrementICU = roundingIncrementICU;
+            } else {
+                actualRoundingIncrementICU = byWidth.equals(BigDecimal.ONE) ? null : byWidth;
+            }
+        } else {
+            if (roundingMode == BigDecimal.ROUND_HALF_EVEN) {
+                actualRoundingIncrementICU = null;
+            } else {
+                if (getMaximumFractionDigits() > 0) {
+                    actualRoundingIncrementICU = BigDecimal.ONE.movePointLeft(getMaximumFractionDigits());
+                }
+            }
+        }
+
+        if (actualRoundingIncrementICU == null) {
+            setRoundingDouble(0.0d);
+            actualRoundingIncrement = null;
+        } else {
+            setRoundingDouble(actualRoundingIncrementICU.doubleValue());
+            actualRoundingIncrement = actualRoundingIncrementICU.toBigDecimal();
+        }
+    }
+
+    static final double roundingIncrementEpsilon = 0.000000001;
+
+    private void setRoundingDouble(double newValue) {
+        roundingDouble = newValue;
+        if (roundingDouble > 0.0d) {
+            double rawRoundedReciprocal = 1.0d / roundingDouble;
+            roundingDoubleReciprocal = Math.rint(rawRoundedReciprocal);
+            if (Math.abs(rawRoundedReciprocal - roundingDoubleReciprocal) > roundingIncrementEpsilon) {
+                roundingDoubleReciprocal = 0.0d;
+            }
+        } else {
+            roundingDoubleReciprocal = 0.0d;
+        }
+    }
 }
 
 // eof