8177552: Compact Number Formatting support
authornishjain
Thu, 06 Dec 2018 12:39:28 +0530
changeset 52869 c5c0db0b7c2f
parent 52868 b4982a22926b
child 52870 a76b7884b59a
8177552: Compact Number Formatting support Reviewed-by: naoto, rriggs
make/jdk/src/classes/build/tools/cldrconverter/AbstractLDMLHandler.java
make/jdk/src/classes/build/tools/cldrconverter/Bundle.java
make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java
make/jdk/src/classes/build/tools/cldrconverter/LDMLParseHandler.java
make/jdk/src/classes/build/tools/cldrconverter/StringListElement.java
make/jdk/src/classes/build/tools/cldrconverter/StringListEntry.java
src/java.base/share/classes/java/text/CompactNumberFormat.java
src/java.base/share/classes/java/text/DecimalFormat.java
src/java.base/share/classes/java/text/NumberFormat.java
src/java.base/share/classes/java/text/spi/NumberFormatProvider.java
src/java.base/share/classes/sun/text/resources/FormatData.java
src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java
src/java.base/share/classes/sun/util/locale/provider/NumberFormatProviderImpl.java
test/jdk/java/text/Format/CompactNumberFormat/CompactFormatAndParseHelper.java
test/jdk/java/text/Format/CompactNumberFormat/TestCNFRounding.java
test/jdk/java/text/Format/CompactNumberFormat/TestCompactNumber.java
test/jdk/java/text/Format/CompactNumberFormat/TestCompactPatternsValidity.java
test/jdk/java/text/Format/CompactNumberFormat/TestEquality.java
test/jdk/java/text/Format/CompactNumberFormat/TestFormatToCharacterIterator.java
test/jdk/java/text/Format/CompactNumberFormat/TestMutatingInstance.java
test/jdk/java/text/Format/CompactNumberFormat/TestParseBigDecimal.java
test/jdk/java/text/Format/CompactNumberFormat/TestSpecialValues.java
test/jdk/java/text/Format/CompactNumberFormat/TestUExtensionOverride.java
test/jdk/java/text/Format/CompactNumberFormat/TestWithCompatProvider.java
test/jdk/java/text/Format/CompactNumberFormat/serialization/TestDeserializeCNF.java
test/jdk/java/text/Format/CompactNumberFormat/serialization/TestSerialization.java
test/jdk/java/text/Format/CompactNumberFormat/serialization/cnf1.ser.txt
test/jdk/java/text/Format/CompactNumberFormat/serialization/cnf2.ser.txt
--- a/make/jdk/src/classes/build/tools/cldrconverter/AbstractLDMLHandler.java	Thu Dec 06 11:54:39 2018 +0530
+++ b/make/jdk/src/classes/build/tools/cldrconverter/AbstractLDMLHandler.java	Thu Dec 06 12:39:28 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -151,6 +151,19 @@
         }
     }
 
+    void pushStringListEntry(String qName, Attributes attributes, String key) {
+        if (!pushIfIgnored(qName, attributes)) {
+            currentContainer = new StringListEntry(qName, currentContainer, key);
+        }
+    }
+
+    void pushStringListElement(String qName, Attributes attributes, int index) {
+        if (!pushIfIgnored(qName, attributes)) {
+            currentContainer = new StringListElement(qName, currentContainer, index);
+        }
+    }
+
+
     private boolean pushIfIgnored(String qName, Attributes attributes) {
         if (isIgnored(attributes) || currentContainer instanceof IgnoredContainer) {
             pushIgnoredContainer(qName);
--- a/make/jdk/src/classes/build/tools/cldrconverter/Bundle.java	Thu Dec 06 11:54:39 2018 +0530
+++ b/make/jdk/src/classes/build/tools/cldrconverter/Bundle.java	Thu Dec 06 12:39:28 2018 +0530
@@ -53,6 +53,10 @@
         "NumberPatterns/percent"
     };
 
+    private final static String[] COMPACT_NUMBER_PATTERN_KEYS = {
+            "short.CompactNumberPatterns",
+            "long.CompactNumberPatterns"};
+
     private final static String[] NUMBER_ELEMENT_KEYS = {
         "NumberElements/decimal",
         "NumberElements/group",
@@ -228,6 +232,16 @@
             }
         }
 
+        for (String k : COMPACT_NUMBER_PATTERN_KEYS) {
+            List<String> patterns = (List<String>) myMap.remove(k);
+            if (patterns != null) {
+                // Replace any null entry with empty strings.
+                String[] arrPatterns = patterns.stream()
+                        .map(s -> s == null ? "" : s).toArray(String[]::new);
+                myMap.put(k, arrPatterns);
+            }
+        }
+
         // if myMap has any of NUMBER_ELEMENT_KEYS, create a complete NumberElements.
         String defaultScript = (String) myMap.get("DefaultNumberingSystem");
         @SuppressWarnings("unchecked")
--- a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java	Thu Dec 06 11:54:39 2018 +0530
+++ b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java	Thu Dec 06 12:39:28 2018 +0530
@@ -888,6 +888,8 @@
             copyIfPresent(map, "NumberElements", formatData);
         }
         copyIfPresent(map, "NumberPatterns", formatData);
+        copyIfPresent(map, "short.CompactNumberPatterns", formatData);
+        copyIfPresent(map, "long.CompactNumberPatterns", formatData);
 
         // put extra number elements for available scripts into formatData, if it is "root"
         if (id.equals("root")) {
--- a/make/jdk/src/classes/build/tools/cldrconverter/LDMLParseHandler.java	Thu Dec 06 11:54:39 2018 +0530
+++ b/make/jdk/src/classes/build/tools/cldrconverter/LDMLParseHandler.java	Thu Dec 06 12:39:28 2018 +0530
@@ -52,6 +52,8 @@
     private final String id;
     private String currentContext = ""; // "format"/"stand-alone"
     private String currentWidth = ""; // "wide"/"narrow"/"abbreviated"
+    private String currentStyle = ""; // short, long for decimalFormat
+    private String compactCount = ""; // one or other for decimalFormat
 
     LDMLParseHandler(String id) {
         this.id = id;
@@ -503,13 +505,85 @@
         // Number format information
         //
         case "decimalFormatLength":
-            if (attributes.getValue("type") == null) {
-                // skipping type="short" data
-                // for FormatData
-                // copy string for later assembly into NumberPatterns
+            String type = attributes.getValue("type");
+            if (null == type) {
+                // format data for decimal number format
                 pushStringEntry(qName, attributes, "NumberPatterns/decimal");
+                currentStyle = type;
             } else {
-                pushIgnoredContainer(qName);
+                switch (type) {
+                    case "short":
+                    case "long":
+                        // considering "short" and long for
+                        // compact number formatting patterns
+                        pushKeyContainer(qName, attributes, type);
+                        currentStyle = type;
+                        break;
+                    default:
+                        pushIgnoredContainer(qName);
+                        break;
+                }
+            }
+            break;
+        case "decimalFormat":
+            if(currentStyle == null) {
+                pushContainer(qName, attributes);
+            } else {
+                switch (currentStyle) {
+                    case "short":
+                        pushStringListEntry(qName, attributes,
+                                currentStyle+".CompactNumberPatterns");
+                        break;
+                    case "long":
+                        pushStringListEntry(qName, attributes,
+                                currentStyle+".CompactNumberPatterns");
+                        break;
+                    default:
+                        pushIgnoredContainer(qName);
+                        break;
+                }
+            }
+            break;
+        case "pattern":
+            String containerName = currentContainer.getqName();
+            if (containerName.equals("decimalFormat")) {
+                if (currentStyle == null) {
+                    pushContainer(qName, attributes);
+                } else {
+                    // The compact number patterns parsing assumes that the order
+                    // of patterns are always in the increasing order of their
+                    // type attribute i.e. type = 1000...
+                    // Between the inflectional forms for a type (e.g.
+                    // count = "one" and count = "other" for type = 1000), it is
+                    // assumed that the count = "one" always appears before
+                    // count = "other"
+                    switch (currentStyle) {
+                        case "short":
+                        case "long":
+                            String count = attributes.getValue("count");
+                            // first pattern of count = "one" or count = "other"
+                            if ((count.equals("one") || count.equals("other"))
+                                    && compactCount.equals("")) {
+                                compactCount = count;
+                                pushStringListElement(qName, attributes,
+                                        (int) Math.log10(Double.parseDouble(attributes.getValue("type"))));
+                            } else if ((count.equals("one") || count.equals("other"))
+                                    && compactCount.equals(count)) {
+                                // extract patterns with similar "count"
+                                // attribute value
+                                pushStringListElement(qName, attributes,
+                                        (int) Math.log10(Double.parseDouble(attributes.getValue("type"))));
+                            } else {
+                                pushIgnoredContainer(qName);
+                            }
+                            break;
+                        default:
+                            pushIgnoredContainer(qName);
+                            break;
+                    }
+                }
+            } else {
+                pushContainer(qName, attributes);
             }
             break;
         case "currencyFormatLength":
@@ -676,10 +750,9 @@
         // "alias" for root
         case "alias":
             {
-                if (id.equals("root") &&
-                        !isIgnored(attributes) &&
-                        currentCalendarType != null &&
-                        !currentCalendarType.lname().startsWith("islamic-")) { // ignore Islamic variants
+                if (id.equals("root") && !isIgnored(attributes)
+                        && ((currentContainer.getqName().equals("decimalFormatLength"))
+                        || (currentCalendarType != null && !currentCalendarType.lname().startsWith("islamic-")))) { // ignore islamic variants
                     pushAliasEntry(qName, attributes, attributes.getValue("path"));
                 } else {
                     pushIgnoredContainer(qName);
@@ -831,6 +904,9 @@
         case "dayPeriods":
         case "eras":
             break;
+        case "decimalFormatLength": // used for compact number formatting patterns
+            keyName = type + ".CompactNumberPatterns";
+            break;
         default:
             keyName = "";
             break;
@@ -869,6 +945,14 @@
             width = path.substring(start+typeKey.length(), path.indexOf("']", start));
         }
 
+        // used for compact number formatting patterns aliases
+        typeKey = "decimalFormatLength[@type='";
+        start = path.indexOf(typeKey);
+        if (start != -1) {
+            String style = path.substring(start + typeKey.length(), path.indexOf("']", start));
+            return toJDKKey(qName, "", style);
+        }
+
         return calType + "." + toJDKKey(qName, context, width);
     }
 
@@ -926,7 +1010,11 @@
             currentContext = "";
             putIfEntry();
             break;
-
+        case "decimalFormatLength":
+            currentStyle = "";
+            compactCount = "";
+            putIfEntry();
+            break;
         default:
             putIfEntry();
         }
@@ -937,22 +1025,28 @@
         if (currentContainer instanceof AliasEntry) {
             Entry<?> entry = (Entry<?>) currentContainer;
             String containerqName = entry.getParent().getqName();
-            Set<String> keyNames = populateAliasKeys(containerqName, currentContext, currentWidth);
-            if (!keyNames.isEmpty()) {
-                for (String keyName : keyNames) {
-                    String[] tmp = keyName.split(",", 3);
-                    String calType = currentCalendarType.lname();
-                    String src = calType+"."+tmp[0];
-                    String target = getTarget(
-                                entry.getKey(),
-                                calType,
-                                tmp[1].length()>0 ? tmp[1] : currentContext,
-                                tmp[2].length()>0 ? tmp[2] : currentWidth);
-                    if (target.substring(target.lastIndexOf('.')+1).equals(containerqName)) {
-                        target = target.substring(0, target.indexOf('.'))+"."+tmp[0];
+            if (containerqName.equals("decimalFormatLength")) {
+                String srcKey = toJDKKey(containerqName, "", currentStyle);
+                String targetKey = getTarget(entry.getKey(), "", "", "");
+                CLDRConverter.aliases.put(srcKey, targetKey);
+            } else {
+                Set<String> keyNames = populateAliasKeys(containerqName, currentContext, currentWidth);
+                if (!keyNames.isEmpty()) {
+                    for (String keyName : keyNames) {
+                        String[] tmp = keyName.split(",", 3);
+                        String calType = currentCalendarType.lname();
+                        String src = calType+"."+tmp[0];
+                        String target = getTarget(
+                                    entry.getKey(),
+                                    calType,
+                                    tmp[1].length()>0 ? tmp[1] : currentContext,
+                                    tmp[2].length()>0 ? tmp[2] : currentWidth);
+                        if (target.substring(target.lastIndexOf('.')+1).equals(containerqName)) {
+                            target = target.substring(0, target.indexOf('.'))+"."+tmp[0];
+                        }
+                        CLDRConverter.aliases.put(src.replaceFirst("^gregorian.", ""),
+                                                  target.replaceFirst("^gregorian.", ""));
                     }
-                    CLDRConverter.aliases.put(src.replaceFirst("^gregorian.", ""),
-                                              target.replaceFirst("^gregorian.", ""));
                 }
             }
         } else if (currentContainer instanceof Entry) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/jdk/src/classes/build/tools/cldrconverter/StringListElement.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package build.tools.cldrconverter;
+
+class StringListElement extends Container {
+
+    StringListEntry list;
+    int index;
+
+    StringListElement(String qName, Container parent, int index) {
+        super(qName, parent);
+        while (!(parent instanceof StringListEntry)) {
+            parent = parent.getParent();
+        }
+        list = (StringListEntry) parent;
+        this.index = index;
+    }
+
+    @Override
+    void addCharacters(char[] characters, int start, int length) {
+        list.addCharacters(index, characters, start, length);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/jdk/src/classes/build/tools/cldrconverter/StringListEntry.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package build.tools.cldrconverter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.IntStream;
+
+class StringListEntry extends Entry<List<String>> {
+
+    private List<String> value;
+
+    StringListEntry(String qName, Container parent, String key) {
+        super(qName, parent, key);
+        value = new ArrayList<>();
+    }
+
+    void addCharacters(int index, char[] characters, int start, int length) {
+        // fill with empty strings when the patterns start from index > 0
+        if (value.size() < index) {
+            IntStream.range(0, index).forEach(i -> value.add(i, ""));
+            value.add(index, new String(characters, start, length));
+        } else {
+            value.add(index, new String(characters, start, length));
+        }
+    }
+
+    @Override
+    List<String> getValue() {
+        for (String element : value) {
+            if (element != null) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/text/CompactNumberFormat.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,2130 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.text;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+
+/**
+ * <p>
+ * {@code CompactNumberFormat} is a concrete subclass of {@code NumberFormat}
+ * that formats a decimal number in its compact form.
+ *
+ * The compact number formatting is designed for the environment where the space
+ * is limited, and the formatted string can be displayed in that limited space.
+ * It is defined by LDML's specification for
+ * <a href = "http://unicode.org/reports/tr35/tr35-numbers.html#Compact_Number_Formats">
+ * Compact Number Formats</a>. A compact number formatting refers
+ * to the representation of a number in a shorter form, based on the patterns
+ * provided for a given locale.
+ *
+ * <p>
+ * For example:
+ * <br>In the {@link java.util.Locale#US US locale}, {@code 1000} can be formatted
+ * as {@code "1K"}, and {@code 1000000} as {@code "1M"}, depending upon the
+ * <a href = "#compact_number_style" >style</a> used.
+ * <br>In the {@code "hi_IN"} locale, {@code 1000} can be formatted as
+ * "1 \u0939\u091C\u093C\u093E\u0930", and {@code 50000000} as "5 \u0915.",
+ * depending upon the <a href = "#compact_number_style" >style</a> used.
+ *
+ * <p>
+ * To obtain a {@code CompactNumberFormat} for a locale, use one
+ * of the factory methods given by {@code NumberFormat} for compact number
+ * formatting. For example,
+ * {@link NumberFormat#getCompactNumberInstance(Locale, Style)}.
+ *
+ * <blockquote><pre>
+ * NumberFormat fmt = NumberFormat.getCompactNumberInstance(
+ *                             new Locale("hi", "IN"), NumberFormat.Style.SHORT);
+ * String result = fmt.format(1000);
+ * </pre></blockquote>
+ *
+ * <h3><a id="compact_number_style">Style</a></h3>
+ * <p>
+ * A number can be formatted in the compact forms with two different
+ * styles, {@link NumberFormat.Style#SHORT SHORT}
+ * and {@link NumberFormat.Style#LONG LONG}. Use
+ * {@link NumberFormat#getCompactNumberInstance(Locale, Style)} for formatting and
+ * parsing a number in {@link NumberFormat.Style#SHORT SHORT} or
+ * {@link NumberFormat.Style#LONG LONG} compact form,
+ * where the given {@code Style} parameter requests the desired
+ * format. A {@link NumberFormat.Style#SHORT SHORT} style
+ * compact number instance in the {@link java.util.Locale#US US locale} formats
+ * {@code 10000} as {@code "10K"}. However, a
+ * {@link NumberFormat.Style#LONG LONG} style instance in same locale
+ * formats {@code 10000} as {@code "10 thousand"}.
+ *
+ * <h3><a id="compact_number_patterns">Compact Number Patterns</a></h3>
+ * <p>
+ * The compact number patterns are represented in a series of patterns where each
+ * pattern is used to format a range of numbers. An example of
+ * {@link NumberFormat.Style#SHORT SHORT} styled compact number patterns
+ * for the {@link java.util.Locale#US US locale} is {@code {"", "", "", "0K",
+ * "00K", "000K", "0M", "00M", "000M", "0B", "00B", "000B", "0T", "00T", "000T"}},
+ * ranging from {@code 10}<sup>{@code 0}</sup> to {@code 10}<sup>{@code 14}</sup>.
+ * There can be any number of patterns and they are
+ * strictly index based starting from the range {@code 10}<sup>{@code 0}</sup>.
+ * For example, in the above patterns, pattern at index 3
+ * ({@code "0K"}) is used for formatting {@code number >= 1000 and number < 10000},
+ * pattern at index 4 ({@code "00K"}) is used for formatting
+ * {@code number >= 10000 and number < 100000} and so on. In most of the locales,
+ * patterns with the range
+ * {@code 10}<sup>{@code 0}</sup>-{@code 10}<sup>{@code 2}</sup> are empty
+ * strings, which implicitly means a special pattern {@code "0"}.
+ * A special pattern {@code "0"} is used for any range which does not contain
+ * a compact pattern. This special pattern can appear explicitly for any specific
+ * range, or considered as a default pattern for an empty string.
+ * <p>
+ * A compact pattern has the following syntax:
+ * <blockquote><pre>
+ * <i>Pattern:</i>
+ *         <i>PositivePattern</i>
+ *         <i>PositivePattern</i> <i>[; NegativePattern]<sub>optional</sub></i>
+ * <i>PositivePattern:</i>
+ *         <i>Prefix<sub>optional</sub></i> <i>MinimumInteger</i> <i>Suffix<sub>optional</sub></i>
+ * <i>NegativePattern:</i>
+ *        <i>Prefix<sub>optional</sub></i> <i>MinimumInteger</i> <i>Suffix<sub>optional</sub></i>
+ * <i>Prefix:</i>
+ *      Any Unicode characters except &#92;uFFFE, &#92;uFFFF, and
+ *      <a href = "DecimalFormat.html#special_pattern_character">special characters</a>
+ * <i>Suffix:</i>
+ *      Any Unicode characters except &#92;uFFFE, &#92;uFFFF, and
+ *      <a href = "DecimalFormat.html#special_pattern_character">special characters</a>
+ * <i>MinimumInteger:</i>
+ *      0
+ *      0 <i>MinimumInteger</i>
+ * </pre></blockquote>
+ *
+ * A compact pattern contains a positive and negative subpattern
+ * separated by a subpattern boundary character {@code ';' (U+003B)},
+ * for example, {@code "0K;-0K"}. Each subpattern has a prefix,
+ * minimum integer digits, and suffix. The negative subpattern
+ * is optional, if absent, then the positive subpattern prefixed with the
+ * minus sign ({@code '-' U+002D HYPHEN-MINUS}) is used as the negative
+ * subpattern. That is, {@code "0K"} alone is equivalent to {@code "0K;-0K"}.
+ * If there is an explicit negative subpattern, it serves only to specify
+ * the negative prefix and suffix. The number of minimum integer digits,
+ * and other characteristics are all the same as the positive pattern.
+ * That means that {@code "0K;-00K"} produces precisely the same behavior
+ * as {@code "0K;-0K"}.
+ *
+ * <p>
+ * Many characters in a compact pattern are taken literally, they are matched
+ * during parsing and output unchanged during formatting.
+ * <a href = "DecimalFormat.html#special_pattern_character">Special characters</a>,
+ * on the other hand, stand for other characters, strings, or classes of
+ * characters. They must be quoted, using single quote {@code ' (U+0027)}
+ * unless noted otherwise, if they are to appear in the prefix or suffix
+ * as literals. For example, 0\u0915'.'.
+ *
+ * <h3>Formatting</h3>
+ * The default formatting behavior returns a formatted string with no fractional
+ * digits, however users can use the {@link #setMinimumFractionDigits(int)}
+ * method to include the fractional part.
+ * The number {@code 1000.0} or {@code 1000} is formatted as {@code "1K"}
+ * not {@code "1.00K"} (in the {@link java.util.Locale#US US locale}). For this
+ * reason, the patterns provided for formatting contain only the minimum
+ * integer digits, prefix and/or suffix, but no fractional part.
+ * For example, patterns used are {@code {"", "", "", 0K, 00K, ...}}. If the pattern
+ * selected for formatting a number is {@code "0"} (special pattern),
+ * either explicit or defaulted, then the general number formatting provided by
+ * {@link java.text.DecimalFormat DecimalFormat}
+ * for the specified locale is used.
+ *
+ * <h3>Parsing</h3>
+ * The default parsing behavior does not allow a grouping separator until
+ * grouping used is set to {@code true} by using
+ * {@link #setGroupingUsed(boolean)}. The parsing of the fractional part
+ * depends on the {@link #isParseIntegerOnly()}. For example, if the
+ * parse integer only is set to true, then the fractional part is skipped.
+ *
+ * <h3>Rounding</h3>
+ * {@code CompactNumberFormat} provides rounding modes defined in
+ * {@link java.math.RoundingMode} for formatting.  By default, it uses
+ * {@link java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}.
+ *
+ * @see CompactNumberFormat.Style
+ * @see NumberFormat
+ * @see DecimalFormat
+ * @since 12
+ */
+public final class CompactNumberFormat extends NumberFormat {
+
+    private static final long serialVersionUID = 7128367218649234678L;
+
+    /**
+     * The patterns for compact form of numbers for this
+     * {@code CompactNumberFormat}. A possible example is
+     * {@code {"", "", "", "0K", "00K", "000K", "0M", "00M", "000M", "0B",
+     * "00B", "000B", "0T", "00T", "000T"}} ranging from
+     * {@code 10}<sup>{@code 0}</sup>-{@code 10}<sup>{@code 14}</sup>,
+     * where each pattern is used to format a range of numbers.
+     * For example, {@code "0K"} is used for formatting
+     * {@code number >= 1000 and number < 10000}, {@code "00K"} is used for
+     * formatting {@code number >= 10000 and number < 100000} and so on.
+     * This field must not be {@code null}.
+     *
+     * @serial
+     */
+    private String[] compactPatterns;
+
+    /**
+     * List of positive prefix patterns of this formatter's
+     * compact number patterns.
+     */
+    private transient List<String> positivePrefixPatterns;
+
+    /**
+     * List of negative prefix patterns of this formatter's
+     * compact number patterns.
+     */
+    private transient List<String> negativePrefixPatterns;
+
+    /**
+     * List of positive suffix patterns of this formatter's
+     * compact number patterns.
+     */
+    private transient List<String> positiveSuffixPatterns;
+
+    /**
+     * List of negative suffix patterns of this formatter's
+     * compact number patterns.
+     */
+    private transient List<String> negativeSuffixPatterns;
+
+    /**
+     * List of divisors of this formatter's compact number patterns.
+     * Divisor can be either Long or BigInteger (if the divisor value goes
+     * beyond long boundary)
+     */
+    private transient List<Number> divisors;
+
+    /**
+     * The {@code DecimalFormatSymbols} object used by this format.
+     * It contains the symbols used to format numbers. For example,
+     * the grouping separator, decimal separator, and so on.
+     * This field must not be {@code null}.
+     *
+     * @serial
+     * @see DecimalFormatSymbols
+     */
+    private DecimalFormatSymbols symbols;
+
+    /**
+     * The decimal pattern which is used for formatting the numbers
+     * matching special pattern "0". This field must not be {@code null}.
+     *
+     * @serial
+     * @see DecimalFormat
+     */
+    private final String decimalPattern;
+
+    /**
+     * A {@code DecimalFormat} used by this format for getting corresponding
+     * general number formatting behavior for compact numbers.
+     *
+     */
+    private transient DecimalFormat decimalFormat;
+
+    /**
+     * A {@code DecimalFormat} used by this format for getting general number
+     * formatting behavior for the numbers which can't be represented as compact
+     * numbers. For example, number matching the special pattern "0" are
+     * formatted through general number format pattern provided by
+     * {@link java.text.DecimalFormat DecimalFormat}
+     * for the specified locale.
+     *
+     */
+    private transient DecimalFormat defaultDecimalFormat;
+
+    /**
+     * The number of digits between grouping separators in the integer portion
+     * of a compact number. For the grouping to work while formatting, this
+     * field needs to be greater than 0 with grouping used set as true.
+     * This field must not be negative.
+     *
+     * @serial
+     */
+    private byte groupingSize = 0;
+
+    /**
+     * Returns whether the {@link #parse(String, ParsePosition)}
+     * method returns {@code BigDecimal}.
+     *
+     * @serial
+     */
+    private boolean parseBigDecimal = false;
+
+    /**
+     * The {@code RoundingMode} used in this compact number format.
+     * This field must not be {@code null}.
+     *
+     * @serial
+     */
+    private RoundingMode roundingMode = RoundingMode.HALF_EVEN;
+
+    /**
+     * Special pattern used for compact numbers
+     */
+    private static final String SPECIAL_PATTERN = "0";
+
+    /**
+     * Multiplier for compact pattern range. In
+     * the list compact patterns each compact pattern
+     * specify the range with the multiplication factor of 10
+     * of its previous compact pattern range.
+     * For example, 10^0, 10^1, 10^2, 10^3, 10^4...
+     *
+     */
+    private static final int RANGE_MULTIPLIER = 10;
+
+    /**
+     * Creates a {@code CompactNumberFormat} using the given decimal pattern,
+     * decimal format symbols and compact patterns.
+     * To obtain the instance of {@code CompactNumberFormat} with the standard
+     * compact patterns for a {@code Locale} and {@code Style},
+     * it is recommended to use the factory methods given by
+     * {@code NumberFormat} for compact number formatting. For example,
+     * {@link NumberFormat#getCompactNumberInstance(Locale, Style)}.
+     *
+     * @param decimalPattern a decimal pattern for general number formatting
+     * @param symbols the set of symbols to be used
+     * @param compactPatterns an array of
+     *        <a href = "CompactNumberFormat.html#compact_number_patterns">
+     *        compact number patterns</a>
+     * @throws NullPointerException if any of the given arguments is
+     *                                 {@code null}
+     * @throws IllegalArgumentException if the given {@code decimalPattern} or the
+     *                     {@code compactPatterns} array contains an invalid pattern
+     *                     or if a {@code null} appears in the array of compact
+     *                     patterns
+     * @see DecimalFormat#DecimalFormat(java.lang.String, DecimalFormatSymbols)
+     * @see DecimalFormatSymbols
+     */
+    public CompactNumberFormat(String decimalPattern,
+            DecimalFormatSymbols symbols, String[] compactPatterns) {
+
+        Objects.requireNonNull(decimalPattern, "decimalPattern");
+        Objects.requireNonNull(symbols, "symbols");
+        Objects.requireNonNull(compactPatterns, "compactPatterns");
+
+        this.symbols = symbols;
+        // Instantiating the DecimalFormat with "0" pattern; this acts just as a
+        // basic pattern; the properties (For example, prefix/suffix)
+        // are later computed based on the compact number formatting process.
+        decimalFormat = new DecimalFormat(SPECIAL_PATTERN, this.symbols);
+
+        // Initializing the super class state with the decimalFormat values
+        // to represent this CompactNumberFormat.
+        // For setting the digits counts, use overridden setXXX methods of this
+        // CompactNumberFormat, as it performs check with the max range allowed
+        // for compact number formatting
+        setMaximumIntegerDigits(decimalFormat.getMaximumIntegerDigits());
+        setMinimumIntegerDigits(decimalFormat.getMinimumIntegerDigits());
+        setMaximumFractionDigits(decimalFormat.getMaximumFractionDigits());
+        setMinimumFractionDigits(decimalFormat.getMinimumFractionDigits());
+
+        super.setGroupingUsed(decimalFormat.isGroupingUsed());
+        super.setParseIntegerOnly(decimalFormat.isParseIntegerOnly());
+
+        this.compactPatterns = compactPatterns;
+
+        // DecimalFormat used for formatting numbers with special pattern "0".
+        // Formatting is delegated to the DecimalFormat's number formatting
+        // with no fraction digits
+        this.decimalPattern = decimalPattern;
+        defaultDecimalFormat = new DecimalFormat(this.decimalPattern,
+                this.symbols);
+        defaultDecimalFormat.setMaximumFractionDigits(0);
+        // Process compact patterns to extract the prefixes, suffixes and
+        // divisors
+        processCompactPatterns();
+    }
+
+    /**
+     * Formats a number to produce a string representing its compact form.
+     * The number can be of any subclass of {@link java.lang.Number}.
+     * @param number     the number to format
+     * @param toAppendTo the {@code StringBuffer} to which the formatted
+     *                   text is to be appended
+     * @param fieldPosition    keeps track on the position of the field within
+     *                         the returned string. For example, for formatting
+     *                         a number {@code 123456789} in the
+     *                         {@link java.util.Locale#US US locale},
+     *                         if the given {@code fieldPosition} is
+     *                         {@link NumberFormat#INTEGER_FIELD}, the begin
+     *                         index and end index of {@code fieldPosition}
+     *                         will be set to 0 and 3, respectively for the
+     *                         output string {@code 123M}. Similarly, positions
+     *                         of the prefix and the suffix fields can be
+     *                         obtained using {@link NumberFormat.Field#PREFIX}
+     *                         and {@link NumberFormat.Field#SUFFIX} respectively.
+     * @return           the {@code StringBuffer} passed in as {@code toAppendTo}
+     * @throws           IllegalArgumentException if {@code number} is
+     *                   {@code null} or not an instance of {@code Number}
+     * @throws           NullPointerException if {@code toAppendTo} or
+     *                   {@code fieldPosition} is {@code null}
+     * @throws           ArithmeticException if rounding is needed with rounding
+     *                   mode being set to {@code RoundingMode.UNNECESSARY}
+     * @see              FieldPosition
+     */
+    @Override
+    public final StringBuffer format(Object number,
+            StringBuffer toAppendTo,
+            FieldPosition fieldPosition) {
+        if (number instanceof Long || number instanceof Integer
+                || number instanceof Short || number instanceof Byte
+                || number instanceof AtomicInteger
+                || number instanceof AtomicLong
+                || (number instanceof BigInteger
+                && ((BigInteger) number).bitLength() < 64)) {
+            return format(((Number) number).longValue(), toAppendTo,
+                    fieldPosition);
+        } else if (number instanceof BigDecimal) {
+            return format((BigDecimal) number, toAppendTo, fieldPosition);
+        } else if (number instanceof BigInteger) {
+            return format((BigInteger) number, toAppendTo, fieldPosition);
+        } else if (number instanceof Number) {
+            return format(((Number) number).doubleValue(), toAppendTo, fieldPosition);
+        } else {
+            throw new IllegalArgumentException("Cannot format "
+                    + number.getClass().getName() + " as a number");
+        }
+    }
+
+    /**
+     * Formats a double to produce a string representing its compact form.
+     * @param number    the double number to format
+     * @param result    where the text is to be appended
+     * @param fieldPosition    keeps track on the position of the field within
+     *                         the returned string. For example, to format
+     *                         a number {@code 1234567.89} in the
+     *                         {@link java.util.Locale#US US locale}
+     *                         if the given {@code fieldPosition} is
+     *                         {@link NumberFormat#INTEGER_FIELD}, the begin
+     *                         index and end index of {@code fieldPosition}
+     *                         will be set to 0 and 1, respectively for the
+     *                         output string {@code 1M}. Similarly, positions
+     *                         of the prefix and the suffix fields can be
+     *                         obtained using {@link NumberFormat.Field#PREFIX}
+     *                         and {@link NumberFormat.Field#SUFFIX} respectively.
+     * @return    the {@code StringBuffer} passed in as {@code result}
+     * @throws NullPointerException if {@code result} or
+     *            {@code fieldPosition} is {@code null}
+     * @throws ArithmeticException if rounding is needed with rounding
+     *            mode being set to {@code RoundingMode.UNNECESSARY}
+     * @see FieldPosition
+     */
+    @Override
+    public StringBuffer format(double number, StringBuffer result,
+            FieldPosition fieldPosition) {
+
+        fieldPosition.setBeginIndex(0);
+        fieldPosition.setEndIndex(0);
+        return format(number, result, fieldPosition.getFieldDelegate());
+    }
+
+    private StringBuffer format(double number, StringBuffer result,
+            FieldDelegate delegate) {
+
+        boolean nanOrInfinity = decimalFormat.handleNaN(number, result, delegate);
+        if (nanOrInfinity) {
+            return result;
+        }
+
+        boolean isNegative = ((number < 0.0)
+                || (number == 0.0 && 1 / number < 0.0));
+
+        nanOrInfinity = decimalFormat.handleInfinity(number, result, delegate, isNegative);
+        if (nanOrInfinity) {
+            return result;
+        }
+
+        // Round the double value with min fraction digits, the integer
+        // part of the rounded value is used for matching the compact
+        // number pattern
+        // For example, if roundingMode is HALF_UP with min fraction
+        // digits = 0, the number 999.6 should round up
+        // to 1000 and outputs 1K/thousand in "en_US" locale
+        DigitList dList = new DigitList();
+        dList.setRoundingMode(getRoundingMode());
+        number = isNegative ? -number : number;
+        dList.set(isNegative, number, getMinimumFractionDigits());
+
+        double roundedNumber = dList.getDouble();
+        int compactDataIndex = selectCompactPattern((long) roundedNumber);
+        if (compactDataIndex != -1) {
+            String prefix = isNegative ? negativePrefixPatterns.get(compactDataIndex)
+                    : positivePrefixPatterns.get(compactDataIndex);
+            String suffix = isNegative ? negativeSuffixPatterns.get(compactDataIndex)
+                    : positiveSuffixPatterns.get(compactDataIndex);
+
+            if (!prefix.isEmpty() || !suffix.isEmpty()) {
+                appendPrefix(result, prefix, delegate);
+                long divisor = (Long) divisors.get(compactDataIndex);
+                roundedNumber = roundedNumber / divisor;
+                decimalFormat.setDigitList(roundedNumber, isNegative, getMaximumFractionDigits());
+                decimalFormat.subformatNumber(result, delegate, isNegative,
+                        false, getMaximumIntegerDigits(), getMinimumIntegerDigits(),
+                        getMaximumFractionDigits(), getMinimumFractionDigits());
+                appendSuffix(result, suffix, delegate);
+            } else {
+                defaultDecimalFormat.doubleSubformat(number, result, delegate, isNegative);
+            }
+        } else {
+            defaultDecimalFormat.doubleSubformat(number, result, delegate, isNegative);
+        }
+        return result;
+    }
+
+    /**
+     * Formats a long to produce a string representing its compact form.
+     * @param number    the long number to format
+     * @param result    where the text is to be appended
+     * @param fieldPosition    keeps track on the position of the field within
+     *                         the returned string. For example, to format
+     *                         a number {@code 123456789} in the
+     *                         {@link java.util.Locale#US US locale},
+     *                         if the given {@code fieldPosition} is
+     *                         {@link NumberFormat#INTEGER_FIELD}, the begin
+     *                         index and end index of {@code fieldPosition}
+     *                         will be set to 0 and 3, respectively for the
+     *                         output string {@code 123M}. Similarly, positions
+     *                         of the prefix and the suffix fields can be
+     *                         obtained using {@link NumberFormat.Field#PREFIX}
+     *                         and {@link NumberFormat.Field#SUFFIX} respectively.
+     * @return       the {@code StringBuffer} passed in as {@code result}
+     * @throws       NullPointerException if {@code result} or
+     *               {@code fieldPosition} is {@code null}
+     * @throws       ArithmeticException if rounding is needed with rounding
+     *               mode being set to {@code RoundingMode.UNNECESSARY}
+     * @see FieldPosition
+     */
+    @Override
+    public StringBuffer format(long number, StringBuffer result,
+            FieldPosition fieldPosition) {
+
+        fieldPosition.setBeginIndex(0);
+        fieldPosition.setEndIndex(0);
+        return format(number, result, fieldPosition.getFieldDelegate());
+    }
+
+    private StringBuffer format(long number, StringBuffer result, FieldDelegate delegate) {
+        boolean isNegative = (number < 0);
+        if (isNegative) {
+            number = -number;
+        }
+
+        if (number < 0) { // LONG_MIN
+            BigInteger bigIntegerValue = BigInteger.valueOf(number);
+            return format(bigIntegerValue, result, delegate, true);
+        }
+
+        int compactDataIndex = selectCompactPattern(number);
+        if (compactDataIndex != -1) {
+            String prefix = isNegative ? negativePrefixPatterns.get(compactDataIndex)
+                    : positivePrefixPatterns.get(compactDataIndex);
+            String suffix = isNegative ? negativeSuffixPatterns.get(compactDataIndex)
+                    : positiveSuffixPatterns.get(compactDataIndex);
+            if (!prefix.isEmpty() || !suffix.isEmpty()) {
+                appendPrefix(result, prefix, delegate);
+                long divisor = (Long) divisors.get(compactDataIndex);
+                if ((number % divisor == 0)) {
+                    number = number / divisor;
+                    decimalFormat.setDigitList(number, isNegative, 0);
+                    decimalFormat.subformatNumber(result, delegate,
+                            isNegative, true, getMaximumIntegerDigits(),
+                            getMinimumIntegerDigits(), getMaximumFractionDigits(),
+                            getMinimumFractionDigits());
+                } else {
+                    // To avoid truncation of fractional part store
+                    // the value in double and follow double path instead of
+                    // long path
+                    double dNumber = (double) number / divisor;
+                    decimalFormat.setDigitList(dNumber, isNegative, getMaximumFractionDigits());
+                    decimalFormat.subformatNumber(result, delegate,
+                            isNegative, false, getMaximumIntegerDigits(),
+                            getMinimumIntegerDigits(), getMaximumFractionDigits(),
+                            getMinimumFractionDigits());
+                }
+                appendSuffix(result, suffix, delegate);
+            } else {
+                number = isNegative ? -number : number;
+                defaultDecimalFormat.format(number, result, delegate);
+            }
+        } else {
+            number = isNegative ? -number : number;
+            defaultDecimalFormat.format(number, result, delegate);
+        }
+        return result;
+    }
+
+    /**
+     * Formats a BigDecimal to produce a string representing its compact form.
+     * @param number    the BigDecimal number to format
+     * @param result    where the text is to be appended
+     * @param fieldPosition    keeps track on the position of the field within
+     *                         the returned string. For example, to format
+     *                         a number {@code 1234567.89} in the
+     *                         {@link java.util.Locale#US US locale},
+     *                         if the given {@code fieldPosition} is
+     *                         {@link NumberFormat#INTEGER_FIELD}, the begin
+     *                         index and end index of {@code fieldPosition}
+     *                         will be set to 0 and 1, respectively for the
+     *                         output string {@code 1M}. Similarly, positions
+     *                         of the prefix and the suffix fields can be
+     *                         obtained using {@link NumberFormat.Field#PREFIX}
+     *                         and {@link NumberFormat.Field#SUFFIX} respectively.
+     * @return        the {@code StringBuffer} passed in as {@code result}
+     * @throws        ArithmeticException if rounding is needed with rounding
+     *                mode being set to {@code RoundingMode.UNNECESSARY}
+     * @throws        NullPointerException if any of the given parameter
+     *                is {@code null}
+     * @see FieldPosition
+     */
+    private StringBuffer format(BigDecimal number, StringBuffer result,
+            FieldPosition fieldPosition) {
+
+        Objects.requireNonNull(number);
+        fieldPosition.setBeginIndex(0);
+        fieldPosition.setEndIndex(0);
+        return format(number, result, fieldPosition.getFieldDelegate());
+    }
+
+    private StringBuffer format(BigDecimal number, StringBuffer result,
+            FieldDelegate delegate) {
+
+        boolean isNegative = number.signum() == -1;
+        if (isNegative) {
+            number = number.negate();
+        }
+
+        // Round the value with min fraction digits, the integer
+        // part of the rounded value is used for matching the compact
+        // number pattern
+        // For example, If roundingMode is HALF_UP with min fraction digits = 0,
+        // the number 999.6 should round up
+        // to 1000 and outputs 1K/thousand in "en_US" locale
+        number = number.setScale(getMinimumFractionDigits(), getRoundingMode());
+
+        int compactDataIndex;
+        if (number.toBigInteger().bitLength() < 64) {
+            compactDataIndex = selectCompactPattern(number.toBigInteger().longValue());
+        } else {
+            compactDataIndex = selectCompactPattern(number.toBigInteger());
+        }
+
+        if (compactDataIndex != -1) {
+            String prefix = isNegative ? negativePrefixPatterns.get(compactDataIndex)
+                    : positivePrefixPatterns.get(compactDataIndex);
+            String suffix = isNegative ? negativeSuffixPatterns.get(compactDataIndex)
+                    : positiveSuffixPatterns.get(compactDataIndex);
+            if (!prefix.isEmpty() || !suffix.isEmpty()) {
+                appendPrefix(result, prefix, delegate);
+                Number divisor = divisors.get(compactDataIndex);
+                number = number.divide(new BigDecimal(divisor.toString()), getRoundingMode());
+                decimalFormat.setDigitList(number, isNegative, getMaximumFractionDigits());
+                decimalFormat.subformatNumber(result, delegate, isNegative,
+                        false, getMaximumIntegerDigits(), getMinimumIntegerDigits(),
+                        getMaximumFractionDigits(), getMinimumFractionDigits());
+                appendSuffix(result, suffix, delegate);
+            } else {
+                number = isNegative ? number.negate() : number;
+                defaultDecimalFormat.format(number, result, delegate);
+            }
+        } else {
+            number = isNegative ? number.negate() : number;
+            defaultDecimalFormat.format(number, result, delegate);
+        }
+        return result;
+    }
+
+    /**
+     * Formats a BigInteger to produce a string representing its compact form.
+     * @param number    the BigInteger number to format
+     * @param result    where the text is to be appended
+     * @param fieldPosition    keeps track on the position of the field within
+     *                         the returned string. For example, to format
+     *                         a number {@code 123456789} in the
+     *                         {@link java.util.Locale#US US locale},
+     *                         if the given {@code fieldPosition} is
+     *                         {@link NumberFormat#INTEGER_FIELD}, the begin index
+     *                         and end index of {@code fieldPosition} will be set
+     *                         to 0 and 3, respectively for the output string
+     *                         {@code 123M}. Similarly, positions of the
+     *                         prefix and the suffix fields can be obtained
+     *                         using {@link NumberFormat.Field#PREFIX} and
+     *                         {@link NumberFormat.Field#SUFFIX} respectively.
+     * @return        the {@code StringBuffer} passed in as {@code result}
+     * @throws        ArithmeticException if rounding is needed with rounding
+     *                mode being set to {@code RoundingMode.UNNECESSARY}
+     * @throws        NullPointerException if any of the given parameter
+     *                is {@code null}
+     * @see FieldPosition
+     */
+    private StringBuffer format(BigInteger number, StringBuffer result,
+            FieldPosition fieldPosition) {
+
+        Objects.requireNonNull(number);
+        fieldPosition.setBeginIndex(0);
+        fieldPosition.setEndIndex(0);
+        return format(number, result, fieldPosition.getFieldDelegate(), false);
+    }
+
+    private StringBuffer format(BigInteger number, StringBuffer result,
+            FieldDelegate delegate, boolean formatLong) {
+
+        boolean isNegative = number.signum() == -1;
+        if (isNegative) {
+            number = number.negate();
+        }
+
+        int compactDataIndex = selectCompactPattern(number);
+        if (compactDataIndex != -1) {
+            String prefix = isNegative ? negativePrefixPatterns.get(compactDataIndex)
+                    : positivePrefixPatterns.get(compactDataIndex);
+            String suffix = isNegative ? negativeSuffixPatterns.get(compactDataIndex)
+                    : positiveSuffixPatterns.get(compactDataIndex);
+            if (!prefix.isEmpty() || !suffix.isEmpty()) {
+                appendPrefix(result, prefix, delegate);
+                Number divisor = divisors.get(compactDataIndex);
+                if (number.mod(new BigInteger(divisor.toString()))
+                        .compareTo(BigInteger.ZERO) == 0) {
+                    number = number.divide(new BigInteger(divisor.toString()));
+
+                    decimalFormat.setDigitList(number, isNegative, 0);
+                    decimalFormat.subformatNumber(result, delegate,
+                            isNegative, true, getMaximumIntegerDigits(),
+                            getMinimumIntegerDigits(), getMaximumFractionDigits(),
+                            getMinimumFractionDigits());
+                } else {
+                    // To avoid truncation of fractional part store the value in
+                    // BigDecimal and follow BigDecimal path instead of
+                    // BigInteger path
+                    BigDecimal nDecimal = new BigDecimal(number)
+                            .divide(new BigDecimal(divisor.toString()), getRoundingMode());
+                    decimalFormat.setDigitList(nDecimal, isNegative, getMaximumFractionDigits());
+                    decimalFormat.subformatNumber(result, delegate,
+                            isNegative, false, getMaximumIntegerDigits(),
+                            getMinimumIntegerDigits(), getMaximumFractionDigits(),
+                            getMinimumFractionDigits());
+                }
+                appendSuffix(result, suffix, delegate);
+            } else {
+                number = isNegative ? number.negate() : number;
+                defaultDecimalFormat.format(number, result, delegate, formatLong);
+            }
+        } else {
+            number = isNegative ? number.negate() : number;
+            defaultDecimalFormat.format(number, result, delegate, formatLong);
+        }
+        return result;
+    }
+
+    /**
+     * Appends the {@code prefix} to the {@code result} and also set the
+     * {@code NumberFormat.Field.SIGN} and {@code NumberFormat.Field.PREFIX}
+     * field positions.
+     * @param result the resulting string, where the pefix is to be appended
+     * @param prefix prefix to append
+     * @param delegate notified of the locations of
+     *                 {@code NumberFormat.Field.SIGN} and
+     *                 {@code NumberFormat.Field.PREFIX} fields
+     */
+    private void appendPrefix(StringBuffer result, String prefix,
+            FieldDelegate delegate) {
+        append(result, expandAffix(prefix), delegate,
+                getFieldPositions(prefix, NumberFormat.Field.PREFIX));
+    }
+
+    /**
+     * Appends {@code suffix} to the {@code result} and also set the
+     * {@code NumberFormat.Field.SIGN} and {@code NumberFormat.Field.SUFFIX}
+     * field positions.
+     * @param result the resulting string, where the suffix is to be appended
+     * @param suffix suffix to append
+     * @param delegate notified of the locations of
+     *                 {@code NumberFormat.Field.SIGN} and
+     *                 {@code NumberFormat.Field.SUFFIX} fields
+     */
+    private void appendSuffix(StringBuffer result, String suffix,
+            FieldDelegate delegate) {
+        append(result, expandAffix(suffix), delegate,
+                getFieldPositions(suffix, NumberFormat.Field.SUFFIX));
+    }
+
+    /**
+     * Appends the {@code string} to the {@code result}.
+     * {@code delegate} is notified of SIGN, PREFIX and/or SUFFIX
+     * field positions.
+     * @param result the resulting string, where the text is to be appended
+     * @param string the text to append
+     * @param delegate notified of the locations of sub fields
+     * @param positions a list of {@code FieldPostion} in the given
+     *                  string
+     */
+    private void append(StringBuffer result, String string,
+            FieldDelegate delegate, List<FieldPosition> positions) {
+        if (string.length() > 0) {
+            int start = result.length();
+            result.append(string);
+            for (int counter = 0; counter < positions.size(); counter++) {
+                FieldPosition fp = positions.get(counter);
+                Format.Field attribute = fp.getFieldAttribute();
+                delegate.formatted(attribute, attribute,
+                        start + fp.getBeginIndex(),
+                        start + fp.getEndIndex(), result);
+            }
+        }
+    }
+
+    /**
+     * Expands an affix {@code pattern} into a string of literals.
+     * All characters in the pattern are literals unless prefixed by QUOTE.
+     * The character prefixed by QUOTE is replaced with its respective
+     * localized literal.
+     * @param pattern a compact number pattern affix
+     * @return an expanded affix
+     */
+    private String expandAffix(String pattern) {
+        // Return if no quoted character exists
+        if (pattern.indexOf(QUOTE) < 0) {
+            return pattern;
+        }
+        StringBuilder sb = new StringBuilder();
+        for (int index = 0; index < pattern.length();) {
+            char ch = pattern.charAt(index++);
+            if (ch == QUOTE) {
+                ch = pattern.charAt(index++);
+                if (ch == MINUS_SIGN) {
+                    ch = symbols.getMinusSign();
+                }
+            }
+            sb.append(ch);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Returns a list of {@code FieldPostion} in the given {@code pattern}.
+     * @param pattern the pattern to be parsed for {@code FieldPosition}
+     * @param field whether a PREFIX or SUFFIX field
+     * @return a list of {@code FieldPostion}
+     */
+    private List<FieldPosition> getFieldPositions(String pattern, Field field) {
+        List<FieldPosition> positions = new ArrayList<>();
+        StringBuilder affix = new StringBuilder();
+        int stringIndex = 0;
+        for (int index = 0; index < pattern.length();) {
+            char ch = pattern.charAt(index++);
+            if (ch == QUOTE) {
+                ch = pattern.charAt(index++);
+                if (ch == MINUS_SIGN) {
+                    ch = symbols.getMinusSign();
+                    FieldPosition fp = new FieldPosition(NumberFormat.Field.SIGN);
+                    fp.setBeginIndex(stringIndex);
+                    fp.setEndIndex(stringIndex + 1);
+                    positions.add(fp);
+                }
+            }
+            stringIndex++;
+            affix.append(ch);
+        }
+        if (affix.length() != 0) {
+            FieldPosition fp = new FieldPosition(field);
+            fp.setBeginIndex(0);
+            fp.setEndIndex(affix.length());
+            positions.add(fp);
+        }
+        return positions;
+    }
+
+    /**
+     * Select the index of the matched compact number pattern for
+     * the given {@code long} {@code number}.
+     *
+     * @param number number to be formatted
+     * @return index of matched compact pattern;
+     *         -1 if no compact patterns specified
+     */
+    private int selectCompactPattern(long number) {
+
+        if (compactPatterns.length == 0) {
+            return -1;
+        }
+
+        // Minimum index can be "0", max index can be "size - 1"
+        int dataIndex = number <= 1 ? 0 : (int) Math.log10(number);
+        dataIndex = Math.min(dataIndex, compactPatterns.length - 1);
+        return dataIndex;
+    }
+
+    /**
+     * Select the index of the matched compact number
+     * pattern for the given {@code BigInteger} {@code number}.
+     *
+     * @param number number to be formatted
+     * @return index of matched compact pattern;
+     *         -1 if no compact patterns specified
+     */
+    private int selectCompactPattern(BigInteger number) {
+
+        int matchedIndex = -1;
+        if (compactPatterns.length == 0) {
+            return matchedIndex;
+        }
+
+        BigInteger currentValue = BigInteger.ONE;
+
+        // For formatting a number, the greatest type less than
+        // or equal to number is used
+        for (int index = 0; index < compactPatterns.length; index++) {
+            if (number.compareTo(currentValue) > 0) {
+                // Input number is greater than current type; try matching with
+                // the next
+                matchedIndex = index;
+                currentValue = currentValue.multiply(BigInteger.valueOf(RANGE_MULTIPLIER));
+                continue;
+            }
+            if (number.compareTo(currentValue) < 0) {
+                // Current type is greater than the input number;
+                // take the previous pattern
+                break;
+            } else {
+                // Equal
+                matchedIndex = index;
+                break;
+            }
+        }
+        return matchedIndex;
+    }
+
+    /**
+     * Formats an Object producing an {@code AttributedCharacterIterator}.
+     * The returned {@code AttributedCharacterIterator} can be used
+     * to build the resulting string, as well as to determine information
+     * about the resulting string.
+     * <p>
+     * Each attribute key of the {@code AttributedCharacterIterator} will
+     * be of type {@code NumberFormat.Field}, with the attribute value
+     * being the same as the attribute key. The prefix and the suffix
+     * parts of the returned iterator (if present) are represented by
+     * the attributes {@link NumberFormat.Field#PREFIX} and
+     * {@link NumberFormat.Field#SUFFIX} respectively.
+     *
+     *
+     * @throws NullPointerException if obj is null
+     * @throws IllegalArgumentException when the Format cannot format the
+     *         given object
+     * @throws ArithmeticException if rounding is needed with rounding
+     *         mode being set to {@code RoundingMode.UNNECESSARY}
+     * @param obj The object to format
+     * @return an {@code AttributedCharacterIterator} describing the
+     *         formatted value
+     */
+    @Override
+    public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
+        CharacterIteratorFieldDelegate delegate
+                = new CharacterIteratorFieldDelegate();
+        StringBuffer sb = new StringBuffer();
+
+        if (obj instanceof Double || obj instanceof Float) {
+            format(((Number) obj).doubleValue(), sb, delegate);
+        } else if (obj instanceof Long || obj instanceof Integer
+                || obj instanceof Short || obj instanceof Byte
+                || obj instanceof AtomicInteger || obj instanceof AtomicLong) {
+            format(((Number) obj).longValue(), sb, delegate);
+        } else if (obj instanceof BigDecimal) {
+            format((BigDecimal) obj, sb, delegate);
+        } else if (obj instanceof BigInteger) {
+            format((BigInteger) obj, sb, delegate, false);
+        } else if (obj == null) {
+            throw new NullPointerException(
+                    "formatToCharacterIterator must be passed non-null object");
+        } else {
+            throw new IllegalArgumentException(
+                    "Cannot format given Object as a Number");
+        }
+        return delegate.getIterator(sb.toString());
+    }
+
+    /**
+     * Computes the divisor using minimum integer digits and
+     * matched pattern index.
+     * @param minIntDigits string of 0s in compact pattern
+     * @param patternIndex index of matched compact pattern
+     * @return divisor value for the number matching the compact
+     *         pattern at given {@code patternIndex}
+     */
+    private Number computeDivisor(String minIntDigits, int patternIndex) {
+        int count = minIntDigits.length() - 1;
+        Number matchedValue;
+        // The divisor value can go above long range, if the compact patterns
+        // goes above index 18, divisor may need to be stored as BigInteger,
+        // since long can't store numbers >= 10^19,
+        if (patternIndex < 19) {
+            matchedValue = (long) Math.pow(RANGE_MULTIPLIER, patternIndex);
+        } else {
+            matchedValue = BigInteger.valueOf(RANGE_MULTIPLIER).pow(patternIndex);
+        }
+        Number divisor = matchedValue;
+        if (count != 0) {
+            if (matchedValue instanceof BigInteger) {
+                BigInteger bigValue = (BigInteger) matchedValue;
+                if (bigValue.compareTo(BigInteger.valueOf((long) Math.pow(RANGE_MULTIPLIER, count))) < 0) {
+                    throw new IllegalArgumentException("Invalid Pattern"
+                            + " [" + compactPatterns[patternIndex]
+                            + "]: min integer digits specified exceeds the limit"
+                            + " for the index " + patternIndex);
+                }
+                divisor = bigValue.divide(BigInteger.valueOf((long) Math.pow(RANGE_MULTIPLIER, count)));
+            } else {
+                long longValue = (long) matchedValue;
+                if (longValue < (long) Math.pow(RANGE_MULTIPLIER, count)) {
+                    throw new IllegalArgumentException("Invalid Pattern"
+                            + " [" + compactPatterns[patternIndex]
+                            + "]: min integer digits specified exceeds the limit"
+                            + " for the index " + patternIndex);
+                }
+                divisor = longValue / (long) Math.pow(RANGE_MULTIPLIER, count);
+            }
+        }
+        return divisor;
+    }
+
+    /**
+     * Process the series of compact patterns to compute the
+     * series of prefixes, suffixes and their respective divisor
+     * value.
+     *
+     */
+    private void processCompactPatterns() {
+        int size = compactPatterns.length;
+        positivePrefixPatterns = new ArrayList<>(size);
+        negativePrefixPatterns = new ArrayList<>(size);
+        positiveSuffixPatterns = new ArrayList<>(size);
+        negativeSuffixPatterns = new ArrayList<>(size);
+        divisors = new ArrayList<>(size);
+
+        for (int index = 0; index < size; index++) {
+            applyPattern(compactPatterns[index], index);
+        }
+    }
+
+    /**
+     * Process a compact pattern at a specific {@code index}
+     * @param pattern the compact pattern to be processed
+     * @param index index in the array of compact patterns
+     *
+     */
+    private void applyPattern(String pattern, int index) {
+
+        int start = 0;
+        boolean gotNegative = false;
+
+        String positivePrefix = "";
+        String positiveSuffix = "";
+        String negativePrefix = "";
+        String negativeSuffix = "";
+        String zeros = "";
+        for (int j = 1; j >= 0 && start < pattern.length(); --j) {
+
+            StringBuffer prefix = new StringBuffer();
+            StringBuffer suffix = new StringBuffer();
+            boolean inQuote = false;
+            // The phase ranges from 0 to 2.  Phase 0 is the prefix.  Phase 1 is
+            // the section of the pattern with digits. Phase 2 is the suffix.
+            // The separation of the characters into phases is
+            // strictly enforced; if phase 1 characters are to appear in the
+            // suffix, for example, they must be quoted.
+            int phase = 0;
+
+            // The affix is either the prefix or the suffix.
+            StringBuffer affix = prefix;
+
+            for (int pos = start; pos < pattern.length(); ++pos) {
+                char ch = pattern.charAt(pos);
+                switch (phase) {
+                    case 0:
+                    case 2:
+                        // Process the prefix / suffix characters
+                        if (inQuote) {
+                            // A quote within quotes indicates either the closing
+                            // quote or two quotes, which is a quote literal. That
+                            // is, we have the second quote in 'do' or 'don''t'.
+                            if (ch == QUOTE) {
+                                if ((pos + 1) < pattern.length()
+                                        && pattern.charAt(pos + 1) == QUOTE) {
+                                    ++pos;
+                                    affix.append("''"); // 'don''t'
+                                } else {
+                                    inQuote = false; // 'do'
+                                }
+                                continue;
+                            }
+                        } else {
+                            // Process unquoted characters seen in prefix or suffix
+                            // phase.
+                            switch (ch) {
+                                case ZERO_DIGIT:
+                                    phase = 1;
+                                    --pos; // Reprocess this character
+                                    continue;
+                                case QUOTE:
+                                    // A quote outside quotes indicates either the
+                                    // opening quote or two quotes, which is a quote
+                                    // literal. That is, we have the first quote in 'do'
+                                    // or o''clock.
+                                    if ((pos + 1) < pattern.length()
+                                            && pattern.charAt(pos + 1) == QUOTE) {
+                                        ++pos;
+                                        affix.append("''"); // o''clock
+                                    } else {
+                                        inQuote = true; // 'do'
+                                    }
+                                    continue;
+                                case SEPARATOR:
+                                    // Don't allow separators before we see digit
+                                    // characters of phase 1, and don't allow separators
+                                    // in the second pattern (j == 0).
+                                    if (phase == 0 || j == 0) {
+                                        throw new IllegalArgumentException(
+                                                "Unquoted special character '"
+                                                + ch + "' in pattern \"" + pattern + "\"");
+                                    }
+                                    start = pos + 1;
+                                    pos = pattern.length();
+                                    continue;
+                                case MINUS_SIGN:
+                                    affix.append("'-");
+                                    continue;
+                                case DECIMAL_SEPARATOR:
+                                case GROUPING_SEPARATOR:
+                                case DIGIT:
+                                case PERCENT:
+                                case PER_MILLE:
+                                case CURRENCY_SIGN:
+                                    throw new IllegalArgumentException(
+                                            "Unquoted special character '" + ch
+                                            + "' in pattern \"" + pattern + "\"");
+                                default:
+                                    break;
+                            }
+                        }
+                        // Note that if we are within quotes, or if this is an
+                        // unquoted, non-special character, then we usually fall
+                        // through to here.
+                        affix.append(ch);
+                        break;
+
+                    case 1:
+                        // The negative subpattern (j = 0) serves only to specify the
+                        // negative prefix and suffix, so all the phase 1 characters,
+                        // for example, digits, zeroDigit, groupingSeparator,
+                        // decimalSeparator, exponent are ignored
+                        if (j == 0) {
+                            while (pos < pattern.length()) {
+                                char negPatternChar = pattern.charAt(pos);
+                                if (negPatternChar == ZERO_DIGIT) {
+                                    ++pos;
+                                } else {
+                                    // Not a phase 1 character, consider it as
+                                    // suffix and parse it in phase 2
+                                    --pos; //process it again in outer loop
+                                    phase = 2;
+                                    affix = suffix;
+                                    break;
+                                }
+                            }
+                            continue;
+                        }
+                        // Consider only '0' as valid pattern char which can appear
+                        // in number part, rest can be either suffix or prefix
+                        if (ch == ZERO_DIGIT) {
+                            zeros = zeros + "0";
+                        } else {
+                            phase = 2;
+                            affix = suffix;
+                            --pos;
+                        }
+                        break;
+                }
+            }
+
+            if (inQuote) {
+                throw new IllegalArgumentException("Invalid single quote"
+                        + " in pattern \"" + pattern + "\"");
+            }
+
+            if (j == 1) {
+                positivePrefix = prefix.toString();
+                positiveSuffix = suffix.toString();
+                negativePrefix = positivePrefix;
+                negativeSuffix = positiveSuffix;
+            } else {
+                negativePrefix = prefix.toString();
+                negativeSuffix = suffix.toString();
+                gotNegative = true;
+            }
+
+            // If there is no negative pattern, or if the negative pattern is
+            // identical to the positive pattern, then prepend the minus sign to
+            // the positive pattern to form the negative pattern.
+            if (!gotNegative
+                    || (negativePrefix.equals(positivePrefix)
+                    && negativeSuffix.equals(positiveSuffix))) {
+                negativeSuffix = positiveSuffix;
+                negativePrefix = "'-" + positivePrefix;
+            }
+        }
+
+        // If no 0s are specified in a non empty pattern, it is invalid
+        if (pattern.length() != 0 && zeros.isEmpty()) {
+            throw new IllegalArgumentException("Invalid pattern"
+                    + " [" + pattern + "]: all patterns must include digit"
+                    + " placement 0s");
+        }
+
+        // Only if positive affix exists; else put empty strings
+        if (!positivePrefix.isEmpty() || !positiveSuffix.isEmpty()) {
+            positivePrefixPatterns.add(positivePrefix);
+            negativePrefixPatterns.add(negativePrefix);
+            positiveSuffixPatterns.add(positiveSuffix);
+            negativeSuffixPatterns.add(negativeSuffix);
+            divisors.add(computeDivisor(zeros, index));
+        } else {
+            positivePrefixPatterns.add("");
+            negativePrefixPatterns.add("");
+            positiveSuffixPatterns.add("");
+            negativeSuffixPatterns.add("");
+            divisors.add(1L);
+        }
+    }
+
+    private final transient DigitList digitList = new DigitList();
+    private static final int STATUS_INFINITE = 0;
+    private static final int STATUS_POSITIVE = 1;
+    private static final int STATUS_LENGTH   = 2;
+
+    private static final char ZERO_DIGIT = '0';
+    private static final char DIGIT = '#';
+    private static final char DECIMAL_SEPARATOR = '.';
+    private static final char GROUPING_SEPARATOR = ',';
+    private static final char MINUS_SIGN = '-';
+    private static final char PERCENT = '%';
+    private static final char PER_MILLE = '\u2030';
+    private static final char SEPARATOR = ';';
+    private static final char CURRENCY_SIGN = '\u00A4';
+    private static final char QUOTE = '\'';
+
+    // Expanded form of positive/negative prefix/suffix,
+    // the expanded form contains special characters in
+    // its localized form, which are used for matching
+    // while parsing a string to number
+    private transient List<String> positivePrefixes;
+    private transient List<String> negativePrefixes;
+    private transient List<String> positiveSuffixes;
+    private transient List<String> negativeSuffixes;
+
+    private void expandAffixPatterns() {
+        positivePrefixes = new ArrayList<>(compactPatterns.length);
+        negativePrefixes = new ArrayList<>(compactPatterns.length);
+        positiveSuffixes = new ArrayList<>(compactPatterns.length);
+        negativeSuffixes = new ArrayList<>(compactPatterns.length);
+        for (int index = 0; index < compactPatterns.length; index++) {
+            positivePrefixes.add(expandAffix(positivePrefixPatterns.get(index)));
+            negativePrefixes.add(expandAffix(negativePrefixPatterns.get(index)));
+            positiveSuffixes.add(expandAffix(positiveSuffixPatterns.get(index)));
+            negativeSuffixes.add(expandAffix(negativeSuffixPatterns.get(index)));
+        }
+    }
+
+    /**
+     * Parses a compact number from a string to produce a {@code Number}.
+     * <p>
+     * The method attempts to parse text starting at the index given by
+     * {@code pos}.
+     * If parsing succeeds, then the index of {@code pos} is updated
+     * to the index after the last character used (parsing does not necessarily
+     * use all characters up to the end of the string), and the parsed
+     * number is returned. The updated {@code pos} can be used to
+     * indicate the starting point for the next call to this method.
+     * If an error occurs, then the index of {@code pos} is not
+     * changed, the error index of {@code pos} is set to the index of
+     * the character where the error occurred, and {@code null} is returned.
+     * <p>
+     * The value is the numeric part in the given text multiplied
+     * by the numeric equivalent of the affix attached
+     * (For example, "K" = 1000 in {@link java.util.Locale#US US locale}).
+     * The subclass returned depends on the value of
+     * {@link #isParseBigDecimal}.
+     * <ul>
+     * <li>If {@link #isParseBigDecimal()} is false (the default),
+     *     most integer values are returned as {@code Long}
+     *     objects, no matter how they are written: {@code "17K"} and
+     *     {@code "17.000K"} both parse to {@code Long.valueOf(17000)}.
+     *     If the value cannot fit into {@code Long}, then the result is
+     *     returned as {@code Double}. This includes values with a
+     *     fractional part, infinite values, {@code NaN},
+     *     and the value -0.0.
+     *     <p>
+     *     Callers may use the {@code Number} methods {@code doubleValue},
+     *     {@code longValue}, etc., to obtain the type they want.
+     *
+     * <li>If {@link #isParseBigDecimal()} is true, values are returned
+     *     as {@code BigDecimal} objects. The special cases negative
+     *     and positive infinity and NaN are returned as {@code Double}
+     *     instances holding the values of the corresponding
+     *     {@code Double} constants.
+     * </ul>
+     * <p>
+     * {@code CompactNumberFormat} parses all Unicode characters that represent
+     * decimal digits, as defined by {@code Character.digit()}. In
+     * addition, {@code CompactNumberFormat} also recognizes as digits the ten
+     * consecutive characters starting with the localized zero digit defined in
+     * the {@code DecimalFormatSymbols} object.
+     * <p>
+     * {@code CompactNumberFormat} parse does not allow parsing scientific
+     * notations. For example, parsing a string {@code "1.05E4K"} in
+     * {@link java.util.Locale#US US locale} breaks at character 'E'
+     * and returns 1.05.
+     *
+     * @param text the string to be parsed
+     * @param pos  a {@code ParsePosition} object with index and error
+     *             index information as described above
+     * @return the parsed value, or {@code null} if the parse fails
+     * @exception  NullPointerException if {@code text} or
+     *             {@code pos} is null
+     *
+     */
+    @Override
+    public Number parse(String text, ParsePosition pos) {
+
+        Objects.requireNonNull(text);
+        Objects.requireNonNull(pos);
+
+        // Lazily expanding the affix patterns, on the first parse
+        // call on this instance
+        // If not initialized, expand and load all affixes
+        if (positivePrefixes == null) {
+            expandAffixPatterns();
+        }
+
+        // The compact number multiplier for parsed string.
+        // Its value is set on parsing prefix and suffix. For example,
+        // in the {@link java.util.Locale#US US locale} parsing {@code "1K"}
+        // sets its value to 1000, as K (thousand) is abbreviated form of 1000.
+        Number cnfMultiplier = 1L;
+
+        // Special case NaN
+        if (text.regionMatches(pos.index, symbols.getNaN(),
+                0, symbols.getNaN().length())) {
+            pos.index = pos.index + symbols.getNaN().length();
+            return Double.NaN;
+        }
+
+        int position = pos.index;
+        int oldStart = pos.index;
+        boolean gotPositive = false;
+        boolean gotNegative = false;
+        int matchedPosIndex = -1;
+        int matchedNegIndex = -1;
+        String matchedPosPrefix = "";
+        String matchedNegPrefix = "";
+        String defaultPosPrefix = defaultDecimalFormat.getPositivePrefix();
+        String defaultNegPrefix = defaultDecimalFormat.getNegativePrefix();
+        // Prefix matching
+        for (int compactIndex = 0; compactIndex < compactPatterns.length; compactIndex++) {
+            String positivePrefix = positivePrefixes.get(compactIndex);
+            String negativePrefix = negativePrefixes.get(compactIndex);
+
+            // Do not break if a match occur; there is a possibility that the
+            // subsequent affixes may match the longer subsequence in the given
+            // string.
+            // For example, matching "Mdx 3" with "M", "Md" as prefix should
+            // match with "Md"
+            boolean match = matchAffix(text, position, positivePrefix,
+                    defaultPosPrefix, matchedPosPrefix);
+            if (match) {
+                matchedPosIndex = compactIndex;
+                matchedPosPrefix = positivePrefix;
+                gotPositive = true;
+            }
+
+            match = matchAffix(text, position, negativePrefix,
+                    defaultNegPrefix, matchedNegPrefix);
+            if (match) {
+                matchedNegIndex = compactIndex;
+                matchedNegPrefix = negativePrefix;
+                gotNegative = true;
+            }
+        }
+
+        // Given text does not match the non empty valid compact prefixes
+        // check with the default prefixes
+        if (!gotPositive && !gotNegative) {
+            if (text.regionMatches(pos.index, defaultPosPrefix, 0,
+                    defaultPosPrefix.length())) {
+                // Matches the default positive prefix
+                matchedPosPrefix = defaultPosPrefix;
+                gotPositive = true;
+            }
+            if (text.regionMatches(pos.index, defaultNegPrefix, 0,
+                    defaultNegPrefix.length())) {
+                // Matches the default negative prefix
+                matchedNegPrefix = defaultNegPrefix;
+                gotNegative = true;
+            }
+        }
+
+        // If both match, take the longest one
+        if (gotPositive && gotNegative) {
+            if (matchedPosPrefix.length() > matchedNegPrefix.length()) {
+                gotNegative = false;
+            } else if (matchedPosPrefix.length() < matchedNegPrefix.length()) {
+                gotPositive = false;
+            }
+        }
+
+        // Update the position and take compact multiplier
+        // only if it matches the compact prefix, not the default
+        // prefix; else multiplier should be 1
+        if (gotPositive) {
+            position += matchedPosPrefix.length();
+            cnfMultiplier = matchedPosIndex != -1
+                    ? divisors.get(matchedPosIndex) : 1L;
+        } else if (gotNegative) {
+            position += matchedNegPrefix.length();
+            cnfMultiplier = matchedNegIndex != -1
+                    ? divisors.get(matchedNegIndex) : 1L;
+        }
+
+        digitList.setRoundingMode(getRoundingMode());
+        boolean[] status = new boolean[STATUS_LENGTH];
+
+        // Call DecimalFormat.subparseNumber() method to parse the
+        // number part of the input text
+        position = decimalFormat.subparseNumber(text, position,
+                digitList, false, false, status);
+
+        if (position == -1) {
+            // Unable to parse the number successfully
+            pos.index = oldStart;
+            pos.errorIndex = oldStart;
+            return null;
+        }
+
+        // If parse integer only is true and the parsing is broken at
+        // decimal point, then pass/ignore all digits and move pointer
+        // at the start of suffix, to process the suffix part
+        if (isParseIntegerOnly()
+                && text.charAt(position) == symbols.getDecimalSeparator()) {
+            position++; // Pass decimal character
+            for (; position < text.length(); ++position) {
+                char ch = text.charAt(position);
+                int digit = ch - symbols.getZeroDigit();
+                if (digit < 0 || digit > 9) {
+                    digit = Character.digit(ch, 10);
+                    // Parse all digit characters
+                    if (!(digit >= 0 && digit <= 9)) {
+                        break;
+                    }
+                }
+            }
+        }
+
+        // Number parsed successfully; match prefix and
+        // suffix to obtain multiplier
+        pos.index = position;
+        Number multiplier = computeParseMultiplier(text, pos,
+                gotPositive ? matchedPosPrefix : matchedNegPrefix,
+                status, gotPositive, gotNegative);
+
+        if (multiplier.longValue() == -1L) {
+            return null;
+        } else if (multiplier.longValue() != 1L) {
+            cnfMultiplier = multiplier;
+        }
+
+        // Special case INFINITY
+        if (status[STATUS_INFINITE]) {
+            if (status[STATUS_POSITIVE]) {
+                return Double.POSITIVE_INFINITY;
+            } else {
+                return Double.NEGATIVE_INFINITY;
+            }
+        }
+
+        if (isParseBigDecimal()) {
+            BigDecimal bigDecimalResult = digitList.getBigDecimal();
+
+            if (cnfMultiplier.longValue() != 1) {
+                bigDecimalResult = bigDecimalResult
+                        .multiply(new BigDecimal(cnfMultiplier.toString()));
+            }
+            if (!status[STATUS_POSITIVE]) {
+                bigDecimalResult = bigDecimalResult.negate();
+            }
+            return bigDecimalResult;
+        } else {
+            Number cnfResult;
+            if (digitList.fitsIntoLong(status[STATUS_POSITIVE], isParseIntegerOnly())) {
+                long longResult = digitList.getLong();
+                cnfResult = generateParseResult(longResult, false,
+                        longResult < 0, status, cnfMultiplier);
+            } else {
+                cnfResult = generateParseResult(digitList.getDouble(),
+                        true, false, status, cnfMultiplier);
+            }
+            return cnfResult;
+        }
+    }
+
+    /**
+     * Returns the parsed result by multiplying the parsed number
+     * with the multiplier representing the prefix and suffix.
+     *
+     * @param number parsed number component
+     * @param gotDouble whether the parsed number contains decimal
+     * @param gotLongMin whether the parsed number is Long.MIN
+     * @param status boolean status flags indicating whether the
+     *               value is infinite and whether it is positive
+     * @param cnfMultiplier compact number multiplier
+     * @return parsed result
+     */
+    private Number generateParseResult(Number number, boolean gotDouble,
+            boolean gotLongMin, boolean[] status, Number cnfMultiplier) {
+
+        if (gotDouble) {
+            if (cnfMultiplier.longValue() != 1L) {
+                double doubleResult = number.doubleValue() * cnfMultiplier.doubleValue();
+                doubleResult = (double) convertIfNegative(doubleResult, status, gotLongMin);
+                // Check if a double can be represeneted as a long
+                long longResult = (long) doubleResult;
+                gotDouble = ((doubleResult != (double) longResult)
+                        || (doubleResult == 0.0 && 1 / doubleResult < 0.0));
+                return gotDouble ? (Number) doubleResult : (Number) longResult;
+            }
+        } else {
+            if (cnfMultiplier.longValue() != 1L) {
+                Number result;
+                if ((cnfMultiplier instanceof Long) && !gotLongMin) {
+                    long longMultiplier = (long) cnfMultiplier;
+                    try {
+                        result = Math.multiplyExact(number.longValue(),
+                                longMultiplier);
+                    } catch (ArithmeticException ex) {
+                        // If number * longMultiplier can not be represented
+                        // as long return as double
+                        result = number.doubleValue() * cnfMultiplier.doubleValue();
+                    }
+                } else {
+                    // cnfMultiplier can not be stored into long or the number
+                    // part is Long.MIN, return as double
+                    result = number.doubleValue() * cnfMultiplier.doubleValue();
+                }
+                return convertIfNegative(result, status, gotLongMin);
+            }
+        }
+
+        // Default number
+        return convertIfNegative(number, status, gotLongMin);
+    }
+
+    /**
+     * Negate the parsed value if the positive status flag is false
+     * and the value is not a Long.MIN
+     * @param number parsed value
+     * @param status boolean status flags indicating whether the
+     *               value is infinite and whether it is positive
+     * @param gotLongMin whether the parsed number is Long.MIN
+     * @return the resulting value
+     */
+    private Number convertIfNegative(Number number, boolean[] status,
+            boolean gotLongMin) {
+
+        if (!status[STATUS_POSITIVE] && !gotLongMin) {
+            if (number instanceof Long) {
+                return -(long) number;
+            } else {
+                return -(double) number;
+            }
+        } else {
+            return number;
+        }
+    }
+
+    /**
+     * Attempts to match the given {@code affix} in the
+     * specified {@code text}.
+     */
+    private boolean matchAffix(String text, int position, String affix,
+            String defaultAffix, String matchedAffix) {
+
+        // Check with the compact affixes which are non empty and
+        // do not match with default affix
+        if (!affix.isEmpty() && !affix.equals(defaultAffix)) {
+            // Look ahead only for the longer match than the previous match
+            if (matchedAffix.length() < affix.length()) {
+                if (text.regionMatches(position, affix, 0, affix.length())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Attempts to match given {@code prefix} and {@code suffix} in
+     * the specified {@code text}.
+     */
+    private boolean matchPrefixAndSuffix(String text, int position, String prefix,
+            String matchedPrefix, String defaultPrefix, String suffix,
+            String matchedSuffix, String defaultSuffix) {
+
+        // Check the compact pattern suffix only if there is a
+        // compact prefix match or a default prefix match
+        // because the compact prefix and suffix should match at the same
+        // index to obtain the multiplier.
+        // The prefix match is required because of the possibility of
+        // same prefix at multiple index, in which case matching the suffix
+        // is used to obtain the single match
+
+        if (prefix.equals(matchedPrefix)
+                || matchedPrefix.equals(defaultPrefix)) {
+            return matchAffix(text, position, suffix, defaultSuffix, matchedSuffix);
+        }
+        return false;
+    }
+
+    /**
+     * Computes multiplier by matching the given {@code matchedPrefix}
+     * and suffix in the specified {@code text} from the lists of
+     * prefixes and suffixes extracted from compact patterns.
+     *
+     * @param text the string to parse
+     * @param parsePosition the {@code ParsePosition} object representing the
+     *                      index and error index of the parse string
+     * @param matchedPrefix prefix extracted which needs to be matched to
+     *                      obtain the multiplier
+     * @param status upon return contains boolean status flags indicating
+     *               whether the value is positive
+     * @param gotPositive based on the prefix parsed; whether the number is positive
+     * @param gotNegative based on the prefix parsed; whether the number is negative
+     * @return the multiplier matching the prefix and suffix; -1 otherwise
+     */
+    private Number computeParseMultiplier(String text, ParsePosition parsePosition,
+            String matchedPrefix, boolean[] status, boolean gotPositive,
+            boolean gotNegative) {
+
+        int position = parsePosition.index;
+        boolean gotPos = false;
+        boolean gotNeg = false;
+        int matchedPosIndex = -1;
+        int matchedNegIndex = -1;
+        String matchedPosSuffix = "";
+        String matchedNegSuffix = "";
+        for (int compactIndex = 0; compactIndex < compactPatterns.length; compactIndex++) {
+            String positivePrefix = positivePrefixes.get(compactIndex);
+            String negativePrefix = negativePrefixes.get(compactIndex);
+            String positiveSuffix = positiveSuffixes.get(compactIndex);
+            String negativeSuffix = negativeSuffixes.get(compactIndex);
+
+            // Do not break if a match occur; there is a possibility that the
+            // subsequent affixes may match the longer subsequence in the given
+            // string.
+            // For example, matching "3Mdx" with "M", "Md" should match with "Md"
+            boolean match = matchPrefixAndSuffix(text, position, positivePrefix, matchedPrefix,
+                    defaultDecimalFormat.getPositivePrefix(), positiveSuffix,
+                    matchedPosSuffix, defaultDecimalFormat.getPositiveSuffix());
+            if (match) {
+                matchedPosIndex = compactIndex;
+                matchedPosSuffix = positiveSuffix;
+                gotPos = true;
+            }
+
+            match = matchPrefixAndSuffix(text, position, negativePrefix, matchedPrefix,
+                    defaultDecimalFormat.getNegativePrefix(), negativeSuffix,
+                    matchedNegSuffix, defaultDecimalFormat.getNegativeSuffix());
+            if (match) {
+                matchedNegIndex = compactIndex;
+                matchedNegSuffix = negativeSuffix;
+                gotNeg = true;
+            }
+        }
+
+        // Suffix in the given text does not match with the compact
+        // patterns suffixes; match with the default suffix
+        if (!gotPos && !gotNeg) {
+            String positiveSuffix = defaultDecimalFormat.getPositiveSuffix();
+            String negativeSuffix = defaultDecimalFormat.getNegativeSuffix();
+            if (text.regionMatches(position, positiveSuffix, 0,
+                    positiveSuffix.length())) {
+                // Matches the default positive prefix
+                matchedPosSuffix = positiveSuffix;
+                gotPos = true;
+            }
+            if (text.regionMatches(position, negativeSuffix, 0,
+                    negativeSuffix.length())) {
+                // Matches the default negative suffix
+                matchedNegSuffix = negativeSuffix;
+                gotNeg = true;
+            }
+        }
+
+        // If both matches, take the longest one
+        if (gotPos && gotNeg) {
+            if (matchedPosSuffix.length() > matchedNegSuffix.length()) {
+                gotNeg = false;
+            } else if (matchedPosSuffix.length() < matchedNegSuffix.length()) {
+                gotPos = false;
+            } else {
+                // If longest comparison fails; take the positive and negative
+                // sign of matching prefix
+                gotPos = gotPositive;
+                gotNeg = gotNegative;
+            }
+        }
+
+        // Fail if neither or both
+        if (gotPos == gotNeg) {
+            parsePosition.errorIndex = position;
+            return -1L;
+        }
+
+        Number cnfMultiplier;
+        // Update the parse position index and take compact multiplier
+        // only if it matches the compact suffix, not the default
+        // suffix; else multiplier should be 1
+        if (gotPos) {
+            parsePosition.index = position + matchedPosSuffix.length();
+            cnfMultiplier = matchedPosIndex != -1
+                    ? divisors.get(matchedPosIndex) : 1L;
+        } else {
+            parsePosition.index = position + matchedNegSuffix.length();
+            cnfMultiplier = matchedNegIndex != -1
+                    ? divisors.get(matchedNegIndex) : 1L;
+        }
+        status[STATUS_POSITIVE] = gotPos;
+        return cnfMultiplier;
+    }
+
+    /**
+     * Reconstitutes this {@code CompactNumberFormat} from a stream
+     * (that is, deserializes it) after performing some validations.
+     * This method throws InvalidObjectException, if the stream data is invalid
+     * because of the following reasons,
+     * <ul>
+     * <li> If any of the {@code decimalPattern}, {@code compactPatterns},
+     * {@code symbols} or {@code roundingMode} is {@code null}.
+     * <li> If the {@code decimalPattern} or the {@code compactPatterns} array
+     * contains an invalid pattern or if a {@code null} appears in the array of
+     * compact patterns.
+     * <li> If the {@code minimumIntegerDigits} is greater than the
+     * {@code maximumIntegerDigits} or the {@code minimumFractionDigits} is
+     * greater than the {@code maximumFractionDigits}. This check is performed
+     * by superclass's Object.
+     * <li> If any of the minimum/maximum integer/fraction digit count is
+     * negative. This check is performed by superclass's readObject.
+     * <li> If the minimum or maximum integer digit count is larger than 309 or
+     * if the minimum or maximum fraction digit count is larger than 340.
+     * <li> If the grouping size is negative or larger than 127.
+     * </ul>
+     *
+     * @param inStream the stream
+     * @throws IOException if an I/O error occurs
+     * @throws ClassNotFoundException if the class of a serialized object
+     *         could not be found
+     */
+    private void readObject(ObjectInputStream inStream) throws IOException,
+            ClassNotFoundException {
+
+        inStream.defaultReadObject();
+        if (decimalPattern == null || compactPatterns == null
+                || symbols == null || roundingMode == null) {
+            throw new InvalidObjectException("One of the 'decimalPattern',"
+                    + " 'compactPatterns', 'symbols' or 'roundingMode'"
+                    + " is null");
+        }
+
+        // Check only the maximum counts because NumberFormat.readObject has
+        // already ensured that the maximum is greater than the minimum count.
+        if (getMaximumIntegerDigits() > DecimalFormat.DOUBLE_INTEGER_DIGITS
+                || getMaximumFractionDigits() > DecimalFormat.DOUBLE_FRACTION_DIGITS) {
+            throw new InvalidObjectException("Digit count out of range");
+        }
+
+        // Check if the grouping size is negative, on an attempt to
+        // put value > 127, it wraps around, so check just negative value
+        if (groupingSize < 0) {
+            throw new InvalidObjectException("Grouping size is negative");
+        }
+
+        try {
+            processCompactPatterns();
+        } catch (IllegalArgumentException ex) {
+            throw new InvalidObjectException(ex.getMessage());
+        }
+
+        decimalFormat = new DecimalFormat(SPECIAL_PATTERN, symbols);
+        decimalFormat.setMaximumFractionDigits(getMaximumFractionDigits());
+        decimalFormat.setMinimumFractionDigits(getMinimumFractionDigits());
+        decimalFormat.setMaximumIntegerDigits(getMaximumIntegerDigits());
+        decimalFormat.setMinimumIntegerDigits(getMinimumIntegerDigits());
+        decimalFormat.setRoundingMode(getRoundingMode());
+        decimalFormat.setGroupingSize(getGroupingSize());
+        decimalFormat.setGroupingUsed(isGroupingUsed());
+        decimalFormat.setParseIntegerOnly(isParseIntegerOnly());
+
+        try {
+            defaultDecimalFormat = new DecimalFormat(decimalPattern, symbols);
+            defaultDecimalFormat.setMaximumFractionDigits(0);
+        } catch (IllegalArgumentException ex) {
+            throw new InvalidObjectException(ex.getMessage());
+        }
+
+    }
+
+    /**
+     * Sets the maximum number of digits allowed in the integer portion of a
+     * number.
+     * The maximum allowed integer range is 309, if the {@code newValue} &gt; 309,
+     * then the maximum integer digits count is set to 309. Negative input
+     * values are replaced with 0.
+     *
+     * @param newValue the maximum number of integer digits to be shown
+     * @see #getMaximumIntegerDigits()
+     */
+    @Override
+    public void setMaximumIntegerDigits(int newValue) {
+        // The maximum integer digits is checked with the allowed range before calling
+        // the DecimalFormat.setMaximumIntegerDigits, which performs the negative check
+        // on the given newValue while setting it as max integer digits.
+        // For example, if a negative value is specified, it is replaced with 0
+        decimalFormat.setMaximumIntegerDigits(Math.min(newValue,
+                DecimalFormat.DOUBLE_INTEGER_DIGITS));
+        super.setMaximumIntegerDigits(decimalFormat.getMaximumIntegerDigits());
+        if (decimalFormat.getMinimumIntegerDigits() > decimalFormat.getMaximumIntegerDigits()) {
+            decimalFormat.setMinimumIntegerDigits(decimalFormat.getMaximumIntegerDigits());
+            super.setMinimumIntegerDigits(decimalFormat.getMinimumIntegerDigits());
+        }
+    }
+
+    /**
+     * Sets the minimum number of digits allowed in the integer portion of a
+     * number.
+     * The maximum allowed integer range is 309, if the {@code newValue} &gt; 309,
+     * then the minimum integer digits count is set to 309. Negative input
+     * values are replaced with 0.
+     *
+     * @param newValue the minimum number of integer digits to be shown
+     * @see #getMinimumIntegerDigits()
+     */
+    @Override
+    public void setMinimumIntegerDigits(int newValue) {
+        // The minimum integer digits is checked with the allowed range before calling
+        // the DecimalFormat.setMinimumIntegerDigits, which performs check on the given
+        // newValue while setting it as min integer digits. For example, if a negative
+        // value is specified, it is replaced with 0
+        decimalFormat.setMinimumIntegerDigits(Math.min(newValue,
+                DecimalFormat.DOUBLE_INTEGER_DIGITS));
+        super.setMinimumIntegerDigits(decimalFormat.getMinimumIntegerDigits());
+        if (decimalFormat.getMinimumIntegerDigits() > decimalFormat.getMaximumIntegerDigits()) {
+            decimalFormat.setMaximumIntegerDigits(decimalFormat.getMinimumIntegerDigits());
+            super.setMaximumIntegerDigits(decimalFormat.getMaximumIntegerDigits());
+        }
+    }
+
+    /**
+     * Sets the minimum number of digits allowed in the fraction portion of a
+     * number.
+     * The maximum allowed fraction range is 340, if the {@code newValue} &gt; 340,
+     * then the minimum fraction digits count is set to 340. Negative input
+     * values are replaced with 0.
+     *
+     * @param newValue the minimum number of fraction digits to be shown
+     * @see #getMinimumFractionDigits()
+     */
+    @Override
+    public void setMinimumFractionDigits(int newValue) {
+        // The minimum fraction digits is checked with the allowed range before
+        // calling the DecimalFormat.setMinimumFractionDigits, which performs
+        // check on the given newValue while setting it as min fraction
+        // digits. For example, if a negative value is specified, it is
+        // replaced with 0
+        decimalFormat.setMinimumFractionDigits(Math.min(newValue,
+                DecimalFormat.DOUBLE_FRACTION_DIGITS));
+        super.setMinimumFractionDigits(decimalFormat.getMinimumFractionDigits());
+        if (decimalFormat.getMinimumFractionDigits() > decimalFormat.getMaximumFractionDigits()) {
+            decimalFormat.setMaximumFractionDigits(decimalFormat.getMinimumFractionDigits());
+            super.setMaximumFractionDigits(decimalFormat.getMaximumFractionDigits());
+        }
+    }
+
+    /**
+     * Sets the maximum number of digits allowed in the fraction portion of a
+     * number.
+     * The maximum allowed fraction range is 340, if the {@code newValue} &gt; 340,
+     * then the maximum fraction digits count is set to 340. Negative input
+     * values are replaced with 0.
+     *
+     * @param newValue the maximum number of fraction digits to be shown
+     * @see #getMaximumFractionDigits()
+     */
+    @Override
+    public void setMaximumFractionDigits(int newValue) {
+        // The maximum fraction digits is checked with the allowed range before
+        // calling the DecimalFormat.setMaximumFractionDigits, which performs
+        // check on the given newValue while setting it as max fraction digits.
+        // For example, if a negative value is specified, it is replaced with 0
+        decimalFormat.setMaximumFractionDigits(Math.min(newValue,
+                DecimalFormat.DOUBLE_FRACTION_DIGITS));
+        super.setMaximumFractionDigits(decimalFormat.getMaximumFractionDigits());
+        if (decimalFormat.getMinimumFractionDigits() > decimalFormat.getMaximumFractionDigits()) {
+            decimalFormat.setMinimumFractionDigits(decimalFormat.getMaximumFractionDigits());
+            super.setMinimumFractionDigits(decimalFormat.getMinimumFractionDigits());
+        }
+    }
+
+    /**
+     * Gets the {@link java.math.RoundingMode} used in this
+     * {@code CompactNumberFormat}.
+     *
+     * @return the {@code RoundingMode} used for this
+     *         {@code CompactNumberFormat}
+     * @see #setRoundingMode(RoundingMode)
+     */
+    @Override
+    public RoundingMode getRoundingMode() {
+        return roundingMode;
+    }
+
+    /**
+     * Sets the {@link java.math.RoundingMode} used in this
+     * {@code CompactNumberFormat}.
+     *
+     * @param roundingMode the {@code RoundingMode} to be used
+     * @see #getRoundingMode()
+     * @throws NullPointerException if {@code roundingMode} is {@code null}
+     */
+    @Override
+    public void setRoundingMode(RoundingMode roundingMode) {
+        decimalFormat.setRoundingMode(roundingMode);
+        this.roundingMode = roundingMode;
+    }
+
+    /**
+     * Returns the grouping size. Grouping size is the number of digits between
+     * grouping separators in the integer portion of a number. For example,
+     * in the compact number {@code "12,347 trillion"} for the
+     * {@link java.util.Locale#US US locale}, the grouping size is 3.
+     *
+     * @return the grouping size
+     * @see #setGroupingSize
+     * @see java.text.NumberFormat#isGroupingUsed
+     * @see java.text.DecimalFormatSymbols#getGroupingSeparator
+     */
+    public int getGroupingSize() {
+        return groupingSize;
+    }
+
+    /**
+     * Sets the grouping size. Grouping size is the number of digits between
+     * grouping separators in the integer portion of a number. For example,
+     * in the compact number {@code "12,347 trillion"} for the
+     * {@link java.util.Locale#US US locale}, the grouping size is 3. The grouping
+     * size must be greater than or equal to zero and less than or equal to 127.
+     *
+     * @param newValue the new grouping size
+     * @see #getGroupingSize
+     * @see java.text.NumberFormat#setGroupingUsed
+     * @see java.text.DecimalFormatSymbols#setGroupingSeparator
+     * @throws IllegalArgumentException if {@code newValue} is negative or
+     * larger than 127
+     */
+    public void setGroupingSize(int newValue) {
+        if (newValue < 0 || newValue > 127) {
+            throw new IllegalArgumentException(
+                    "The value passed is negative or larger than 127");
+        }
+        groupingSize = (byte) newValue;
+        decimalFormat.setGroupingSize(groupingSize);
+    }
+
+    /**
+     * Returns true if grouping is used in this format. For example, with
+     * grouping on and grouping size set to 3, the number {@code 12346567890987654}
+     * can be formatted as {@code "12,347 trillion"} in the
+     * {@link java.util.Locale#US US locale}.
+     * The grouping separator is locale dependent.
+     *
+     * @return {@code true} if grouping is used;
+     *         {@code false} otherwise
+     * @see #setGroupingUsed
+     */
+    @Override
+    public boolean isGroupingUsed() {
+        return super.isGroupingUsed();
+    }
+
+    /**
+     * Sets whether or not grouping will be used in this format.
+     *
+     * @param newValue {@code true} if grouping is used;
+     *                 {@code false} otherwise
+     * @see #isGroupingUsed
+     */
+    @Override
+    public void setGroupingUsed(boolean newValue) {
+        decimalFormat.setGroupingUsed(newValue);
+        super.setGroupingUsed(newValue);
+    }
+
+    /**
+     * Returns true if this format parses only an integer from the number
+     * component of a compact number.
+     * Parsing an integer means that only an integer is considered from the
+     * number component, prefix/suffix is still considered to compute the
+     * resulting output.
+     * For example, in the {@link java.util.Locale#US US locale}, if this method
+     * returns {@code true}, the string {@code "1234.78 thousand"} would be
+     * parsed as the value {@code 1234000} (1234 (integer part) * 1000
+     * (thousand)) and the fractional part would be skipped.
+     * The exact format accepted by the parse operation is locale dependent.
+     *
+     * @return {@code true} if compact numbers should be parsed as integers
+     *         only; {@code false} otherwise
+     */
+    @Override
+    public boolean isParseIntegerOnly() {
+        return super.isParseIntegerOnly();
+    }
+
+    /**
+     * Sets whether or not this format parses only an integer from the number
+     * component of a compact number.
+     *
+     * @param value {@code true} if compact numbers should be parsed as
+     *              integers only; {@code false} otherwise
+     * @see #isParseIntegerOnly
+     */
+    @Override
+    public void setParseIntegerOnly(boolean value) {
+        decimalFormat.setParseIntegerOnly(value);
+        super.setParseIntegerOnly(value);
+    }
+
+    /**
+     * Returns whether the {@link #parse(String, ParsePosition)}
+     * method returns {@code BigDecimal}. The default value is false.
+     *
+     * @return {@code true} if the parse method returns BigDecimal;
+     *         {@code false} otherwise
+     * @see #setParseBigDecimal
+     *
+     */
+    public boolean isParseBigDecimal() {
+        return parseBigDecimal;
+    }
+
+    /**
+     * Sets whether the {@link #parse(String, ParsePosition)}
+     * method returns {@code BigDecimal}.
+     *
+     * @param newValue {@code true} if the parse method returns BigDecimal;
+     *                 {@code false} otherwise
+     * @see #isParseBigDecimal
+     *
+     */
+    public void setParseBigDecimal(boolean newValue) {
+        parseBigDecimal = newValue;
+    }
+
+    /**
+     * Checks if this {@code CompactNumberFormat} is equal to the
+     * specified {@code obj}. The objects of type {@code CompactNumberFormat}
+     * are compared, other types return false; obeys the general contract of
+     * {@link java.lang.Object#equals(java.lang.Object) Object.equals}.
+     *
+     * @param obj the object to compare with
+     * @return true if this is equal to the other {@code CompactNumberFormat}
+     */
+    @Override
+    public boolean equals(Object obj) {
+
+        if (!super.equals(obj)) {
+            return false;
+        }
+
+        CompactNumberFormat other = (CompactNumberFormat) obj;
+        return decimalPattern.equals(other.decimalPattern)
+                && symbols.equals(other.symbols)
+                && Arrays.equals(compactPatterns, other.compactPatterns)
+                && roundingMode.equals(other.roundingMode)
+                && groupingSize == other.groupingSize
+                && parseBigDecimal == other.parseBigDecimal;
+    }
+
+    /**
+     * Returns the hash code for this {@code CompactNumberFormat} instance.
+     *
+     * @return hash code for this {@code CompactNumberFormat}
+     */
+    @Override
+    public int hashCode() {
+        return 31 * super.hashCode() +
+                Objects.hash(decimalPattern, symbols, roundingMode)
+                + Arrays.hashCode(compactPatterns) + groupingSize
+                + Boolean.hashCode(parseBigDecimal);
+    }
+
+    /**
+     * Creates and returns a copy of this {@code CompactNumberFormat}
+     * instance.
+     *
+     * @return a clone of this instance
+     */
+    @Override
+    public CompactNumberFormat clone() {
+        CompactNumberFormat other = (CompactNumberFormat) super.clone();
+        other.compactPatterns = compactPatterns.clone();
+        other.symbols = (DecimalFormatSymbols) symbols.clone();
+        return other;
+    }
+
+}
+
--- a/src/java.base/share/classes/java/text/DecimalFormat.java	Thu Dec 06 11:54:39 2018 +0530
+++ b/src/java.base/share/classes/java/text/DecimalFormat.java	Thu Dec 06 12:39:28 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -48,9 +48,6 @@
 import java.util.ArrayList;
 import java.util.Currency;
 import java.util.Locale;
-import java.util.ResourceBundle;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 import sun.util.locale.provider.LocaleProviderAdapter;
@@ -157,7 +154,7 @@
  * used. So <code>"#,##,###,####"</code> == <code>"######,####"</code> ==
  * <code>"##,####,####"</code>.
  *
- * <h4>Special Pattern Characters</h4>
+ * <h4><a id="special_pattern_character">Special Pattern Characters</a></h4>
  *
  * <p>Many characters in a pattern are taken literally; they are matched during
  * parsing and output unchanged during formatting.  Special characters, on the
@@ -572,14 +569,11 @@
      *                  mode being set to RoundingMode.UNNECESSARY
      * @return The formatted number string
      */
-    private StringBuffer format(double number, StringBuffer result,
+    StringBuffer format(double number, StringBuffer result,
                                 FieldDelegate delegate) {
-        if (Double.isNaN(number) ||
-           (Double.isInfinite(number) && multiplier == 0)) {
-            int iFieldStart = result.length();
-            result.append(symbols.getNaN());
-            delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
-                               iFieldStart, result.length(), result);
+
+        boolean nanOrInfinity = handleNaN(number, result, delegate);
+        if (nanOrInfinity) {
             return result;
         }
 
@@ -599,6 +593,56 @@
             number *= multiplier;
         }
 
+        nanOrInfinity = handleInfinity(number, result, delegate, isNegative);
+        if (nanOrInfinity) {
+            return result;
+        }
+
+        if (isNegative) {
+            number = -number;
+        }
+
+        // at this point we are guaranteed a nonnegative finite number.
+        assert (number >= 0 && !Double.isInfinite(number));
+        return doubleSubformat(number, result, delegate, isNegative);
+    }
+
+    /**
+     * Checks if the given {@code number} is {@code Double.NaN}. if yes;
+     * appends the NaN symbol to the result string. The NaN string is
+     * determined by the DecimalFormatSymbols object.
+     * @param number the double number to format
+     * @param result where the text is to be appended
+     * @param delegate notified of locations of sub fields
+     * @return true, if number is a NaN; false otherwise
+     */
+    boolean handleNaN(double number, StringBuffer result,
+            FieldDelegate delegate) {
+        if (Double.isNaN(number)
+                || (Double.isInfinite(number) && multiplier == 0)) {
+            int iFieldStart = result.length();
+            result.append(symbols.getNaN());
+            delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
+                    iFieldStart, result.length(), result);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Checks if the given {@code number} is {@code Double.NEGATIVE_INFINITY}
+     * or {@code Double.POSITIVE_INFINITY}. if yes;
+     * appends the infinity string to the result string. The infinity string is
+     * determined by the DecimalFormatSymbols object.
+     * @param number the double number to format
+     * @param result where the text is to be appended
+     * @param delegate notified of locations of sub fields
+     * @param isNegative whether the given {@code number} is negative
+     * @return true, if number is a {@code Double.NEGATIVE_INFINITY} or
+     *         {@code Double.POSITIVE_INFINITY}; false otherwise
+     */
+    boolean handleInfinity(double number, StringBuffer result,
+            FieldDelegate delegate, boolean isNegative) {
         if (Double.isInfinite(number)) {
             if (isNegative) {
                 append(result, negativePrefix, delegate,
@@ -621,27 +665,24 @@
                        getPositiveSuffixFieldPositions(), Field.SIGN);
             }
 
-            return result;
-        }
-
-        if (isNegative) {
-            number = -number;
+            return true;
         }
-
-        // at this point we are guaranteed a nonnegative finite number.
-        assert(number >= 0 && !Double.isInfinite(number));
-
-        synchronized(digitList) {
+        return false;
+    }
+
+    StringBuffer doubleSubformat(double number, StringBuffer result,
+            FieldDelegate delegate, boolean isNegative) {
+        synchronized (digitList) {
             int maxIntDigits = super.getMaximumIntegerDigits();
             int minIntDigits = super.getMinimumIntegerDigits();
             int maxFraDigits = super.getMaximumFractionDigits();
             int minFraDigits = super.getMinimumFractionDigits();
 
-            digitList.set(isNegative, number, useExponentialNotation ?
-                          maxIntDigits + maxFraDigits : maxFraDigits,
-                          !useExponentialNotation);
+            digitList.set(isNegative, number, useExponentialNotation
+                    ? maxIntDigits + maxFraDigits : maxFraDigits,
+                    !useExponentialNotation);
             return subformat(result, delegate, isNegative, false,
-                       maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
+                    maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
         }
     }
 
@@ -683,7 +724,7 @@
      *                   mode being set to RoundingMode.UNNECESSARY
      * @see java.text.FieldPosition
      */
-    private StringBuffer format(long number, StringBuffer result,
+    StringBuffer format(long number, StringBuffer result,
                                FieldDelegate delegate) {
         boolean isNegative = (number < 0);
         if (isNegative) {
@@ -774,7 +815,7 @@
      *                   mode being set to RoundingMode.UNNECESSARY
      * @return The formatted number string
      */
-    private StringBuffer format(BigDecimal number, StringBuffer result,
+    StringBuffer format(BigDecimal number, StringBuffer result,
                                 FieldDelegate delegate) {
         if (multiplier != 1) {
             number = number.multiply(getBigDecimalMultiplier());
@@ -835,7 +876,7 @@
      *                   mode being set to RoundingMode.UNNECESSARY
      * @see java.text.FieldPosition
      */
-    private StringBuffer format(BigInteger number, StringBuffer result,
+    StringBuffer format(BigInteger number, StringBuffer result,
                                FieldDelegate delegate, boolean formatLong) {
         if (multiplier != 1) {
             number = number.multiply(getBigIntegerMultiplier());
@@ -917,7 +958,7 @@
         return delegate.getIterator(sb.toString());
     }
 
-    // ==== Begin fast-path formating logic for double =========================
+    // ==== Begin fast-path formatting logic for double =========================
 
     /* Fast-path formatting will be used for format(double ...) methods iff a
      * number of conditions are met (see checkAndSetFastPathStatus()):
@@ -1662,6 +1703,26 @@
 
     }
 
+    /**
+     * Sets the {@code DigitList} used by this {@code DecimalFormat}
+     * instance.
+     * @param number the number to format
+     * @param isNegative true, if the number is negative; false otherwise
+     * @param maxDigits the max digits
+     */
+    void setDigitList(Number number, boolean isNegative, int maxDigits) {
+
+        if (number instanceof Double) {
+            digitList.set(isNegative, (Double) number, maxDigits, true);
+        } else if (number instanceof BigDecimal) {
+            digitList.set(isNegative, (BigDecimal) number, maxDigits, true);
+        } else if (number instanceof Long) {
+            digitList.set(isNegative, (Long) number, maxDigits);
+        } else if (number instanceof BigInteger) {
+            digitList.set(isNegative, (BigInteger) number, maxDigits);
+        }
+    }
+
     // ======== End fast-path formating logic for double =========================
 
     /**
@@ -1669,29 +1730,59 @@
      * be filled in with the correct digits.
      */
     private StringBuffer subformat(StringBuffer result, FieldDelegate delegate,
-                                   boolean isNegative, boolean isInteger,
-                                   int maxIntDigits, int minIntDigits,
-                                   int maxFraDigits, int minFraDigits) {
-        // NOTE: This isn't required anymore because DigitList takes care of this.
-        //
-        //  // The negative of the exponent represents the number of leading
-        //  // zeros between the decimal and the first non-zero digit, for
-        //  // a value < 0.1 (e.g., for 0.00123, -fExponent == 2).  If this
-        //  // is more than the maximum fraction digits, then we have an underflow
-        //  // for the printed representation.  We recognize this here and set
-        //  // the DigitList representation to zero in this situation.
-        //
-        //  if (-digitList.decimalAt >= getMaximumFractionDigits())
-        //  {
-        //      digitList.count = 0;
-        //  }
-
+            boolean isNegative, boolean isInteger,
+            int maxIntDigits, int minIntDigits,
+            int maxFraDigits, int minFraDigits) {
+
+        // Process prefix
+        if (isNegative) {
+            append(result, negativePrefix, delegate,
+                    getNegativePrefixFieldPositions(), Field.SIGN);
+        } else {
+            append(result, positivePrefix, delegate,
+                    getPositivePrefixFieldPositions(), Field.SIGN);
+        }
+
+        // Process number
+        subformatNumber(result, delegate, isNegative, isInteger,
+                maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
+
+        // Process suffix
+        if (isNegative) {
+            append(result, negativeSuffix, delegate,
+                    getNegativeSuffixFieldPositions(), Field.SIGN);
+        } else {
+            append(result, positiveSuffix, delegate,
+                    getPositiveSuffixFieldPositions(), Field.SIGN);
+        }
+
+        return result;
+    }
+
+    /**
+     * Subformats number part using the {@code DigitList} of this
+     * {@code DecimalFormat} instance.
+     * @param result where the text is to be appended
+     * @param delegate notified of the location of sub fields
+     * @param isNegative true, if the number is negative; false otherwise
+     * @param isInteger true, if the number is an integer; false otherwise
+     * @param maxIntDigits maximum integer digits
+     * @param minIntDigits minimum integer digits
+     * @param maxFraDigits maximum fraction digits
+     * @param minFraDigits minimum fraction digits
+     */
+    void subformatNumber(StringBuffer result, FieldDelegate delegate,
+            boolean isNegative, boolean isInteger,
+            int maxIntDigits, int minIntDigits,
+            int maxFraDigits, int minFraDigits) {
+
+        char grouping = symbols.getGroupingSeparator();
         char zero = symbols.getZeroDigit();
         int zeroDelta = zero - '0'; // '0' is the DigitList representation of zero
-        char grouping = symbols.getGroupingSeparator();
+
         char decimal = isCurrencyFormat ?
-            symbols.getMonetaryDecimalSeparator() :
-            symbols.getDecimalSeparator();
+                symbols.getMonetaryDecimalSeparator() :
+                symbols.getDecimalSeparator();
 
         /* Per bug 4147706, DecimalFormat must respect the sign of numbers which
          * format as zero.  This allows sensible computations and preserves
@@ -1703,14 +1794,6 @@
             digitList.decimalAt = 0; // Normalize
         }
 
-        if (isNegative) {
-            append(result, negativePrefix, delegate,
-                   getNegativePrefixFieldPositions(), Field.SIGN);
-        } else {
-            append(result, positivePrefix, delegate,
-                   getPositivePrefixFieldPositions(), Field.SIGN);
-        }
-
         if (useExponentialNotation) {
             int iFieldStart = result.length();
             int iFieldEnd = -1;
@@ -1719,7 +1802,6 @@
             // 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".
-
             // 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
@@ -1782,8 +1864,8 @@
                     fFieldStart = result.length();
                 }
                 result.append((i < digitList.count) ?
-                              (char)(digitList.digits[i] + zeroDelta) :
-                              zero);
+                        (char)(digitList.digits[i] + zeroDelta) :
+                        zero);
             }
 
             if (decimalSeparatorAlwaysShown && totalDigits == integerDigits) {
@@ -1802,17 +1884,17 @@
                 iFieldEnd = result.length();
             }
             delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
-                               iFieldStart, iFieldEnd, result);
+                    iFieldStart, iFieldEnd, result);
             if (addedDecimalSeparator) {
                 delegate.formatted(Field.DECIMAL_SEPARATOR,
-                                   Field.DECIMAL_SEPARATOR,
-                                   iFieldEnd, fFieldStart, result);
+                        Field.DECIMAL_SEPARATOR,
+                        iFieldEnd, fFieldStart, result);
             }
             if (fFieldStart == -1) {
                 fFieldStart = result.length();
             }
             delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION,
-                               fFieldStart, result.length(), result);
+                    fFieldStart, result.length(), result);
 
             // The exponent is output using the pattern-specified minimum
             // exponent digits.  There is no maximum limit to the exponent
@@ -1823,7 +1905,7 @@
             result.append(symbols.getExponentSeparator());
 
             delegate.formatted(Field.EXPONENT_SYMBOL, Field.EXPONENT_SYMBOL,
-                               fieldStart, result.length(), result);
+                    fieldStart, result.length(), result);
 
             // For zero values, we force the exponent to zero.  We
             // must do this here, and not earlier, because the value
@@ -1838,7 +1920,7 @@
                 fieldStart = result.length();
                 result.append(symbols.getMinusSign());
                 delegate.formatted(Field.EXPONENT_SIGN, Field.EXPONENT_SIGN,
-                                   fieldStart, result.length(), result);
+                        fieldStart, result.length(), result);
             }
             digitList.set(negativeExponent, exponent);
 
@@ -1849,10 +1931,10 @@
             }
             for (int i=0; i<digitList.decimalAt; ++i) {
                 result.append((i < digitList.count) ?
-                          (char)(digitList.digits[i] + zeroDelta) : zero);
+                        (char)(digitList.digits[i] + zeroDelta) : zero);
             }
             delegate.formatted(Field.EXPONENT, Field.EXPONENT, eFieldStart,
-                               result.length(), result);
+                    result.length(), result);
         } else {
             int iFieldStart = result.length();
 
@@ -1889,19 +1971,19 @@
                 // grouping separator if i==0 though; that's at the end of
                 // the integer part.
                 if (isGroupingUsed() && i>0 && (groupingSize != 0) &&
-                    (i % groupingSize == 0)) {
+                        (i % groupingSize == 0)) {
                     int gStart = result.length();
                     result.append(grouping);
                     delegate.formatted(Field.GROUPING_SEPARATOR,
-                                       Field.GROUPING_SEPARATOR, gStart,
-                                       result.length(), result);
+                            Field.GROUPING_SEPARATOR, gStart,
+                            result.length(), result);
                 }
             }
 
             // Determine whether or not there are any printable fractional
             // digits.  If we've used up the digits we know there aren't.
             boolean fractionPresent = (minFraDigits > 0) ||
-                (!isInteger && digitIndex < digitList.count);
+                    (!isInteger && digitIndex < digitList.count);
 
             // If there is no fraction present, and we haven't printed any
             // integer digits, then print a zero.  Otherwise we won't print
@@ -1911,7 +1993,7 @@
             }
 
             delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
-                               iFieldStart, result.length(), result);
+                    iFieldStart, result.length(), result);
 
             // Output the decimal separator if we always do so.
             int sStart = result.length();
@@ -1921,8 +2003,8 @@
 
             if (sStart != result.length()) {
                 delegate.formatted(Field.DECIMAL_SEPARATOR,
-                                   Field.DECIMAL_SEPARATOR,
-                                   sStart, result.length(), result);
+                        Field.DECIMAL_SEPARATOR,
+                        sStart, result.length(), result);
             }
             int fFieldStart = result.length();
 
@@ -1934,7 +2016,7 @@
                 // we have an integer, so there is no fractional stuff to
                 // display, or we're out of significant digits.
                 if (i >= minFraDigits &&
-                    (isInteger || digitIndex >= digitList.count)) {
+                        (isInteger || digitIndex >= digitList.count)) {
                     break;
                 }
 
@@ -1957,18 +2039,8 @@
 
             // Record field information for caller.
             delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION,
-                               fFieldStart, result.length(), result);
+                    fFieldStart, result.length(), result);
         }
-
-        if (isNegative) {
-            append(result, negativeSuffix, delegate,
-                   getNegativeSuffixFieldPositions(), Field.SIGN);
-        } else {
-            append(result, positiveSuffix, delegate,
-                   getPositiveSuffixFieldPositions(), Field.SIGN);
-        }
-
-        return result;
     }
 
     /**
@@ -2209,19 +2281,18 @@
      * whether the value was infinite and whether it was positive.
      */
     private final boolean subparse(String text, ParsePosition parsePosition,
-                   String positivePrefix, String negativePrefix,
-                   DigitList digits, boolean isExponent,
-                   boolean status[]) {
+                                   String positivePrefix, String negativePrefix,
+                                   DigitList digits, boolean isExponent,
+                                   boolean status[]) {
         int position = parsePosition.index;
         int oldStart = parsePosition.index;
-        int backup;
         boolean gotPositive, gotNegative;
 
         // check for positivePrefix; take longest
         gotPositive = text.regionMatches(position, positivePrefix, 0,
-                                         positivePrefix.length());
+                positivePrefix.length());
         gotNegative = text.regionMatches(position, negativePrefix, 0,
-                                         negativePrefix.length());
+                negativePrefix.length());
 
         if (gotPositive && gotNegative) {
             if (positivePrefix.length() > negativePrefix.length()) {
@@ -2240,10 +2311,75 @@
             return false;
         }
 
+        position = subparseNumber(text, position, digits, true, isExponent, status);
+        if (position == -1) {
+            parsePosition.index = oldStart;
+            parsePosition.errorIndex = oldStart;
+            return false;
+        }
+
+        // Check for suffix
+        if (!isExponent) {
+            if (gotPositive) {
+                gotPositive = text.regionMatches(position,positiveSuffix,0,
+                        positiveSuffix.length());
+            }
+            if (gotNegative) {
+                gotNegative = text.regionMatches(position,negativeSuffix,0,
+                        negativeSuffix.length());
+            }
+
+            // If both match, take longest
+            if (gotPositive && gotNegative) {
+                if (positiveSuffix.length() > negativeSuffix.length()) {
+                    gotNegative = false;
+                } else if (positiveSuffix.length() < negativeSuffix.length()) {
+                    gotPositive = false;
+                }
+            }
+
+            // Fail if neither or both
+            if (gotPositive == gotNegative) {
+                parsePosition.errorIndex = position;
+                return false;
+            }
+
+            parsePosition.index = position +
+                    (gotPositive ? positiveSuffix.length() : negativeSuffix.length()); // mark success!
+        } else {
+            parsePosition.index = position;
+        }
+
+        status[STATUS_POSITIVE] = gotPositive;
+        if (parsePosition.index == oldStart) {
+            parsePosition.errorIndex = position;
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Parses a number from the given {@code text}. The text is parsed
+     * beginning at position, until an unparseable character is seen.
+     *
+     * @param text the string to parse
+     * @param position the position at which parsing begins
+     * @param digits the DigitList to set to the parsed value
+     * @param checkExponent whether to check for exponential number
+     * @param isExponent if the exponential part is encountered
+     * @param status upon return contains boolean status flags indicating
+     *               whether the value is infinite and whether it is
+     *               positive
+     * @return returns the position of the first unparseable character or
+     *         -1 in case of no valid number parsed
+     */
+    int subparseNumber(String text, int position,
+                       DigitList digits, boolean checkExponent,
+                       boolean isExponent, boolean status[]) {
         // process digits or Inf, find decimal position
         status[STATUS_INFINITE] = false;
         if (!isExponent && text.regionMatches(position,symbols.getInfinity(),0,
-                          symbols.getInfinity().length())) {
+                symbols.getInfinity().length())) {
             position += symbols.getInfinity().length();
             status[STATUS_INFINITE] = true;
         } else {
@@ -2257,8 +2393,8 @@
             digits.decimalAt = digits.count = 0;
             char zero = symbols.getZeroDigit();
             char decimal = isCurrencyFormat ?
-                symbols.getMonetaryDecimalSeparator() :
-                symbols.getDecimalSeparator();
+                    symbols.getMonetaryDecimalSeparator() :
+                    symbols.getDecimalSeparator();
             char grouping = symbols.getGroupingSeparator();
             String exponentString = symbols.getExponentSeparator();
             boolean sawDecimal = false;
@@ -2270,7 +2406,7 @@
             // pin when the maximum allowable digits is reached.
             int digitCount = 0;
 
-            backup = -1;
+            int backup = -1;
             for (; position < text.length(); ++position) {
                 char ch = text.charAt(position);
 
@@ -2334,15 +2470,15 @@
                     // require that they be followed by a digit.  Otherwise
                     // we backup and reprocess them.
                     backup = position;
-                } else if (!isExponent && text.regionMatches(position, exponentString, 0, exponentString.length())
-                             && !sawExponent) {
+                } else if (checkExponent && !isExponent && text.regionMatches(position, exponentString, 0, exponentString.length())
+                        && !sawExponent) {
                     // Process the exponent by recursively calling this method.
-                     ParsePosition pos = new ParsePosition(position + exponentString.length());
+                    ParsePosition pos = new ParsePosition(position + exponentString.length());
                     boolean[] stat = new boolean[STATUS_LENGTH];
                     DigitList exponentDigits = new DigitList();
 
                     if (subparse(text, pos, "", Character.toString(symbols.getMinusSign()), exponentDigits, true, stat) &&
-                        exponentDigits.fitsIntoLong(stat[STATUS_POSITIVE], true)) {
+                            exponentDigits.fitsIntoLong(stat[STATUS_POSITIVE], true)) {
                         position = pos.index; // Advance past the exponent
                         exponent = (int)exponentDigits.getLong();
                         if (!stat[STATUS_POSITIVE]) {
@@ -2373,50 +2509,11 @@
             // parse "$" with pattern "$#0.00". (return index 0 and error
             // index 1).
             if (!sawDigit && digitCount == 0) {
-                parsePosition.index = oldStart;
-                parsePosition.errorIndex = oldStart;
-                return false;
+                return -1;
             }
         }
-
-        // check for suffix
-        if (!isExponent) {
-            if (gotPositive) {
-                gotPositive = text.regionMatches(position,positiveSuffix,0,
-                                                 positiveSuffix.length());
-            }
-            if (gotNegative) {
-                gotNegative = text.regionMatches(position,negativeSuffix,0,
-                                                 negativeSuffix.length());
-            }
-
-        // if both match, take longest
-        if (gotPositive && gotNegative) {
-            if (positiveSuffix.length() > negativeSuffix.length()) {
-                gotNegative = false;
-            } else if (positiveSuffix.length() < negativeSuffix.length()) {
-                gotPositive = false;
-            }
-        }
-
-        // fail if neither or both
-        if (gotPositive == gotNegative) {
-            parsePosition.errorIndex = position;
-            return false;
-        }
-
-        parsePosition.index = position +
-            (gotPositive ? positiveSuffix.length() : negativeSuffix.length()); // mark success!
-        } else {
-            parsePosition.index = position;
-        }
-
-        status[STATUS_POSITIVE] = gotPositive;
-        if (parsePosition.index == oldStart) {
-            parsePosition.errorIndex = position;
-            return false;
-        }
-        return true;
+        return position;
+
     }
 
     /**
--- a/src/java.base/share/classes/java/text/NumberFormat.java	Thu Dec 06 11:54:39 2018 +0530
+++ b/src/java.base/share/classes/java/text/NumberFormat.java	Thu Dec 06 12:39:28 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -47,13 +47,11 @@
 import java.text.spi.NumberFormatProvider;
 import java.util.Currency;
 import java.util.HashMap;
-import java.util.Hashtable;
 import java.util.Locale;
 import java.util.Map;
-import java.util.ResourceBundle;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
-import java.util.spi.LocaleServiceProvider;
 import sun.util.locale.provider.LocaleProviderAdapter;
 import sun.util.locale.provider.LocaleServiceProviderPool;
 
@@ -112,9 +110,12 @@
  * Use <code>getInstance</code> or <code>getNumberInstance</code> to get the
  * normal number format. Use <code>getIntegerInstance</code> to get an
  * integer number format. Use <code>getCurrencyInstance</code> to get the
- * currency number format. And use <code>getPercentInstance</code> to get a
- * format for displaying percentages. With this format, a fraction like
- * 0.53 is displayed as 53%.
+ * currency number format. Use {@code getCompactNumberInstance} to get the
+ * compact number format to format a number in shorter form. For example,
+ * {@code 2000} can be formatted as {@code "2K"} in
+ * {@link java.util.Locale#US US locale}. Use <code>getPercentInstance</code>
+ * to get a format for displaying percentages. With this format, a fraction
+ * like 0.53 is displayed as 53%.
  *
  * <p>
  * You can also control the display of numbers with such methods as
@@ -122,9 +123,10 @@
  * If you want even more control over the format or parsing,
  * or want to give your users more control,
  * you can try casting the <code>NumberFormat</code> you get from the factory methods
- * to a <code>DecimalFormat</code>. This will work for the vast majority
- * of locales; just remember to put it in a <code>try</code> block in case you
- * encounter an unusual one.
+ * to a {@code DecimalFormat} or {@code CompactNumberFormat} depending on
+ * the factory method used. This will work for the vast majority of locales;
+ * just remember to put it in a <code>try</code> block in case you encounter
+ * an unusual one.
  *
  * <p>
  * NumberFormat and DecimalFormat are designed such that some controls
@@ -201,6 +203,7 @@
  *
  * @see          DecimalFormat
  * @see          ChoiceFormat
+ * @see          CompactNumberFormat
  * @author       Mark Davis
  * @author       Helena Shih
  * @since 1.1
@@ -472,7 +475,7 @@
      * formatting
      */
     public static final NumberFormat getInstance() {
-        return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE);
+        return getInstance(Locale.getDefault(Locale.Category.FORMAT), null, NUMBERSTYLE);
     }
 
     /**
@@ -485,7 +488,7 @@
      * formatting
      */
     public static NumberFormat getInstance(Locale inLocale) {
-        return getInstance(inLocale, NUMBERSTYLE);
+        return getInstance(inLocale, null, NUMBERSTYLE);
     }
 
     /**
@@ -501,7 +504,7 @@
      * @see java.util.Locale.Category#FORMAT
      */
     public static final NumberFormat getNumberInstance() {
-        return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE);
+        return getInstance(Locale.getDefault(Locale.Category.FORMAT), null, NUMBERSTYLE);
     }
 
     /**
@@ -512,7 +515,7 @@
      * formatting
      */
     public static NumberFormat getNumberInstance(Locale inLocale) {
-        return getInstance(inLocale, NUMBERSTYLE);
+        return getInstance(inLocale, null, NUMBERSTYLE);
     }
 
     /**
@@ -534,7 +537,7 @@
      * @since 1.4
      */
     public static final NumberFormat getIntegerInstance() {
-        return getInstance(Locale.getDefault(Locale.Category.FORMAT), INTEGERSTYLE);
+        return getInstance(Locale.getDefault(Locale.Category.FORMAT), null, INTEGERSTYLE);
     }
 
     /**
@@ -551,7 +554,7 @@
      * @since 1.4
      */
     public static NumberFormat getIntegerInstance(Locale inLocale) {
-        return getInstance(inLocale, INTEGERSTYLE);
+        return getInstance(inLocale, null, INTEGERSTYLE);
     }
 
     /**
@@ -566,7 +569,7 @@
      * @see java.util.Locale.Category#FORMAT
      */
     public static final NumberFormat getCurrencyInstance() {
-        return getInstance(Locale.getDefault(Locale.Category.FORMAT), CURRENCYSTYLE);
+        return getInstance(Locale.getDefault(Locale.Category.FORMAT), null, CURRENCYSTYLE);
     }
 
     /**
@@ -576,7 +579,7 @@
      * @return the {@code NumberFormat} instance for currency formatting
      */
     public static NumberFormat getCurrencyInstance(Locale inLocale) {
-        return getInstance(inLocale, CURRENCYSTYLE);
+        return getInstance(inLocale, null, CURRENCYSTYLE);
     }
 
     /**
@@ -591,7 +594,7 @@
      * @see java.util.Locale.Category#FORMAT
      */
     public static final NumberFormat getPercentInstance() {
-        return getInstance(Locale.getDefault(Locale.Category.FORMAT), PERCENTSTYLE);
+        return getInstance(Locale.getDefault(Locale.Category.FORMAT), null, PERCENTSTYLE);
     }
 
     /**
@@ -601,14 +604,14 @@
      * @return the {@code NumberFormat} instance for percentage formatting
      */
     public static NumberFormat getPercentInstance(Locale inLocale) {
-        return getInstance(inLocale, PERCENTSTYLE);
+        return getInstance(inLocale, null, PERCENTSTYLE);
     }
 
     /**
      * Returns a scientific format for the current default locale.
      */
     /*public*/ final static NumberFormat getScientificInstance() {
-        return getInstance(Locale.getDefault(Locale.Category.FORMAT), SCIENTIFICSTYLE);
+        return getInstance(Locale.getDefault(Locale.Category.FORMAT), null, SCIENTIFICSTYLE);
     }
 
     /**
@@ -617,7 +620,50 @@
      * @param inLocale the desired locale
      */
     /*public*/ static NumberFormat getScientificInstance(Locale inLocale) {
-        return getInstance(inLocale, SCIENTIFICSTYLE);
+        return getInstance(inLocale, null, SCIENTIFICSTYLE);
+    }
+
+    /**
+     * Returns a compact number format for the default
+     * {@link java.util.Locale.Category#FORMAT FORMAT} locale with
+     * {@link NumberFormat.Style#SHORT "SHORT"} format style.
+     *
+     * @return A {@code NumberFormat} instance for compact number
+     *         formatting
+     *
+     * @see CompactNumberFormat
+     * @see NumberFormat.Style
+     * @see java.util.Locale#getDefault(java.util.Locale.Category)
+     * @see java.util.Locale.Category#FORMAT
+     * @since 12
+     */
+    public static NumberFormat getCompactNumberInstance() {
+        return getInstance(Locale.getDefault(
+                Locale.Category.FORMAT), NumberFormat.Style.SHORT, COMPACTSTYLE);
+    }
+
+    /**
+     * Returns a compact number format for the specified {@link java.util.Locale locale}
+     * and {@link NumberFormat.Style formatStyle}.
+     *
+     * @param locale the desired locale
+     * @param formatStyle the style for formatting a number
+     * @return A {@code NumberFormat} instance for compact number
+     *         formatting
+     * @throws NullPointerException if {@code locale} or {@code formatStyle}
+     *                              is {@code null}
+     *
+     * @see CompactNumberFormat
+     * @see NumberFormat.Style
+     * @see java.util.Locale
+     * @since 12
+     */
+    public static NumberFormat getCompactNumberInstance(Locale locale,
+            NumberFormat.Style formatStyle) {
+
+        Objects.requireNonNull(locale);
+        Objects.requireNonNull(formatStyle);
+        return getInstance(locale, formatStyle, COMPACTSTYLE);
     }
 
     /**
@@ -900,20 +946,22 @@
     // =======================privates===============================
 
     private static NumberFormat getInstance(Locale desiredLocale,
-                                           int choice) {
+                                            Style formatStyle, int choice) {
         LocaleProviderAdapter adapter;
         adapter = LocaleProviderAdapter.getAdapter(NumberFormatProvider.class,
-                                                   desiredLocale);
-        NumberFormat numberFormat = getInstance(adapter, desiredLocale, choice);
+                desiredLocale);
+        NumberFormat numberFormat = getInstance(adapter, desiredLocale,
+                formatStyle, choice);
         if (numberFormat == null) {
             numberFormat = getInstance(LocaleProviderAdapter.forJRE(),
-                                       desiredLocale, choice);
+                    desiredLocale, formatStyle, choice);
         }
         return numberFormat;
     }
 
     private static NumberFormat getInstance(LocaleProviderAdapter adapter,
-                                            Locale locale, int choice) {
+                                            Locale locale, Style formatStyle,
+                                            int choice) {
         NumberFormatProvider provider = adapter.getNumberFormatProvider();
         NumberFormat numberFormat = null;
         switch (choice) {
@@ -929,6 +977,9 @@
         case INTEGERSTYLE:
             numberFormat = provider.getIntegerInstance(locale);
             break;
+        case COMPACTSTYLE:
+            numberFormat = provider.getCompactNumberInstance(locale, formatStyle);
+            break;
         }
         return numberFormat;
     }
@@ -1001,6 +1052,7 @@
     private static final int PERCENTSTYLE = 2;
     private static final int SCIENTIFICSTYLE = 3;
     private static final int INTEGERSTYLE = 4;
+    private static final int COMPACTSTYLE = 5;
 
     /**
      * True if the grouping (i.e. thousands) separator is used when
@@ -1276,5 +1328,43 @@
          * Constant identifying the exponent sign field.
          */
         public static final Field EXPONENT_SIGN = new Field("exponent sign");
+
+        /**
+         * Constant identifying the prefix field.
+         *
+         * @since 12
+         */
+        public static final Field PREFIX = new Field("prefix");
+
+        /**
+         * Constant identifying the suffix field.
+         *
+         * @since 12
+         */
+        public static final Field SUFFIX = new Field("suffix");
+    }
+
+    /**
+     * A number format style.
+     * <p>
+     * {@code Style} is an enum which represents the style for formatting
+     * a number within a given {@code NumberFormat} instance.
+     *
+     * @see CompactNumberFormat
+     * @see NumberFormat#getCompactNumberInstance(Locale, Style)
+     * @since 12
+     */
+    public enum Style {
+
+        /**
+         * The {@code SHORT} number format style.
+         */
+        SHORT,
+
+        /**
+         * The {@code LONG} number format style.
+         */
+        LONG
+
     }
 }
--- a/src/java.base/share/classes/java/text/spi/NumberFormatProvider.java	Thu Dec 06 11:54:39 2018 +0530
+++ b/src/java.base/share/classes/java/text/spi/NumberFormatProvider.java	Thu Dec 06 12:39:28 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -110,4 +110,37 @@
      * @see java.text.NumberFormat#getPercentInstance(java.util.Locale)
      */
     public abstract NumberFormat getPercentInstance(Locale locale);
+
+    /**
+     * Returns a new {@code NumberFormat} instance which formats
+     * a number in its compact form for the specified
+     * {@code locale} and {@code formatStyle}.
+     *
+     * @implSpec The default implementation of this method throws
+     * {@code UnSupportedOperationException}. Overriding the implementation
+     * of this method returns the compact number formatter instance
+     * of the given {@code locale} with specified {@code formatStyle}.
+     *
+     * @param locale the desired locale
+     * @param formatStyle the style for formatting a number
+     * @throws NullPointerException if {@code locale} or {@code formatStyle}
+     *     is {@code null}
+     * @throws IllegalArgumentException if {@code locale} is not
+     *     one of the locales returned from
+     *     {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
+     *     getAvailableLocales()}.
+     * @return a compact number formatter
+     *
+     * @see java.text.NumberFormat#getCompactNumberInstance(Locale,
+     *                      NumberFormat.Style)
+     * @since 12
+     */
+    public NumberFormat getCompactNumberInstance(Locale locale,
+            NumberFormat.Style formatStyle) {
+        throw new UnsupportedOperationException(
+                "The " + this.getClass().getName() + " should override this"
+                + " method to return compact number format instance of "
+                + locale + " locale and " + formatStyle + " style.");
+    }
+
 }
--- a/src/java.base/share/classes/sun/text/resources/FormatData.java	Thu Dec 06 11:54:39 2018 +0530
+++ b/src/java.base/share/classes/sun/text/resources/FormatData.java	Thu Dec 06 12:39:28 2018 +0530
@@ -796,6 +796,44 @@
                     "NaN",
                 }
             },
+            { "short.CompactNumberPatterns",
+                new String[] {
+                    "",
+                    "",
+                    "",
+                    "0K",
+                    "00K",
+                    "000K",
+                    "0M",
+                    "00M",
+                    "000M",
+                    "0B",
+                    "00B",
+                    "000B",
+                    "0T",
+                    "00T",
+                    "000T",
+                }
+            },
+            { "long.CompactNumberPatterns",
+                new String[] {
+                    "",
+                    "",
+                    "",
+                    "0 thousand",
+                    "00 thousand",
+                    "000 thousand",
+                    "0 million",
+                    "00 million",
+                    "000 million",
+                    "0 billion",
+                    "00 billion",
+                    "000 billion",
+                    "0 trillion",
+                    "00 trillion",
+                    "000 trillion",
+                }
+            },
             { "TimePatterns",
                 new String[] {
                     "h:mm:ss a z",        // full time pattern
--- a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java	Thu Dec 06 11:54:39 2018 +0530
+++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java	Thu Dec 06 12:39:28 2018 +0530
@@ -43,6 +43,7 @@
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.SoftReference;
 import java.text.MessageFormat;
+import java.text.NumberFormat;
 import java.util.Calendar;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -88,6 +89,7 @@
     private static final String ZONE_IDS_CACHEKEY = "ZID";
     private static final String CALENDAR_NAMES = "CALN.";
     private static final String NUMBER_PATTERNS_CACHEKEY = "NP";
+    private static final String COMPACT_NUMBER_PATTERNS_CACHEKEY = "CNP";
     private static final String DATE_TIME_PATTERN = "DTP.";
 
     // TimeZoneNamesBundle exemplar city prefix
@@ -479,6 +481,32 @@
     }
 
     /**
+     * Returns the compact number format patterns.
+     * @param formatStyle the style for formatting a number
+     * @return an array of compact number patterns
+     */
+    @SuppressWarnings("unchecked")
+    public String[] getCNPatterns(NumberFormat.Style formatStyle) {
+
+        Objects.requireNonNull(formatStyle);
+        String[] compactNumberPatterns = null;
+        removeEmptyReferences();
+        String width = (formatStyle == NumberFormat.Style.LONG) ? "long" : "short";
+        String cacheKey = width + "." + COMPACT_NUMBER_PATTERNS_CACHEKEY;
+        ResourceReference data = cache.get(cacheKey);
+        if (data == null || ((compactNumberPatterns
+                = (String[]) data.get()) == null)) {
+            ResourceBundle resource = localeData.getNumberFormatData(locale);
+            compactNumberPatterns = (String[]) resource
+                    .getObject(width + ".CompactNumberPatterns");
+            cache.put(cacheKey, new ResourceReference(cacheKey,
+                    (Object) compactNumberPatterns, referenceQueue));
+        }
+        return compactNumberPatterns;
+    }
+
+
+    /**
      * Returns the FormatData resource bundle of this LocaleResources.
      * The FormatData should be used only for accessing extra
      * resources required by JSR 310.
--- a/src/java.base/share/classes/sun/util/locale/provider/NumberFormatProviderImpl.java	Thu Dec 06 11:54:39 2018 +0530
+++ b/src/java.base/share/classes/sun/util/locale/provider/NumberFormatProviderImpl.java	Thu Dec 06 12:39:28 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -40,12 +40,14 @@
 
 package sun.util.locale.provider;
 
+import java.text.CompactNumberFormat;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 import java.text.NumberFormat;
 import java.text.spi.NumberFormatProvider;
 import java.util.Currency;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -225,6 +227,49 @@
         }
     }
 
+    /**
+     * Returns a new {@code NumberFormat} instance which formats
+     * a number in its compact form for the specified
+     * {@code locale} and {@code formatStyle}.
+     *
+     * @param locale the desired locale
+     * @param formatStyle the style for formatting a number
+     * @throws NullPointerException if {@code locale} or {@code formatStyle}
+     *     is {@code null}
+     * @throws IllegalArgumentException if {@code locale} isn't
+     *     one of the locales returned from
+     *     {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()
+     *     getAvailableLocales()}.
+     * @return a compact number formatter
+     *
+     * @see java.text.NumberFormat#getCompactNumberInstance(Locale,
+     *                      NumberFormat.Style)
+     * @since 12
+     */
+    @Override
+    public NumberFormat getCompactNumberInstance(Locale locale,
+            NumberFormat.Style formatStyle) {
+
+        Objects.requireNonNull(locale);
+        Objects.requireNonNull(formatStyle);
+
+        // Check for region override
+        Locale override = locale.getUnicodeLocaleType("nu") == null
+                ? CalendarDataUtility.findRegionOverride(locale)
+                : locale;
+
+        LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type);
+        LocaleResources resource = adapter.getLocaleResources(override);
+
+        String[] numberPatterns = resource.getNumberPatterns();
+        DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(override);
+        String[] cnPatterns = resource.getCNPatterns(formatStyle);
+
+        CompactNumberFormat format = new CompactNumberFormat(numberPatterns[0],
+                symbols, cnPatterns);
+        return format;
+    }
+
     @Override
     public Set<String> getAvailableLanguageTags() {
         return langtags;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/CompactFormatAndParseHelper.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.math.BigDecimal;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import static org.testng.Assert.*;
+
+class CompactFormatAndParseHelper {
+
+    static void testFormat(NumberFormat cnf, Object number,
+            String expected) {
+        String result = cnf.format(number);
+        assertEquals(result, expected, "Incorrect formatting of the number '"
+                + number + "'");
+    }
+
+    static void testParse(NumberFormat cnf, String parseString,
+            Number expected, ParsePosition position, Class<? extends Number> returnType) throws ParseException {
+
+        Number number;
+        if (position == null) {
+            number = cnf.parse(parseString);
+        } else {
+            number = cnf.parse(parseString, position);
+        }
+
+        if (returnType != null) {
+            assertEquals(number.getClass(), returnType, "Incorrect return type for string" + parseString);
+        }
+
+        if (number instanceof Double) {
+            assertEquals(number.doubleValue(), (double) expected,
+                    "Incorrect parsing of the string '" + parseString + "'");
+        } else if (number instanceof Long) {
+            assertEquals(number.longValue(), (long) expected,
+                    "Incorrect parsing of the string '" + parseString + "'");
+        } else if (number instanceof BigDecimal) {
+            BigDecimal num = (BigDecimal) number;
+            assertEquals(num, (BigDecimal) expected,
+                    "Incorrect parsing of the string '" + parseString + "'");
+        } else {
+            assertEquals(number, expected, "Incorrect parsing of the string '"
+                    + parseString + "'");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/TestCNFRounding.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8177552
+ * @summary Checks the rounding of formatted number in compact number formatting
+ * @run testng/othervm TestCNFRounding
+ */
+
+import java.math.RoundingMode;
+import java.text.NumberFormat;
+import java.util.List;
+import java.util.Locale;
+import static org.testng.Assert.*;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class TestCNFRounding {
+
+    private static final List<RoundingMode> MODES = List.of(
+            RoundingMode.HALF_EVEN,
+            RoundingMode.HALF_UP,
+            RoundingMode.HALF_DOWN,
+            RoundingMode.UP,
+            RoundingMode.DOWN,
+            RoundingMode.CEILING,
+            RoundingMode.FLOOR);
+
+    @DataProvider(name = "roundingData")
+    Object[][] roundingData() {
+        return new Object[][]{
+            // Number, half_even, half_up, half_down, up, down, ceiling, floor
+            {5500, new String[]{"6K", "6K", "5K", "6K", "5K", "6K", "5K"}},
+            {2500, new String[]{"2K", "3K", "2K", "3K", "2K", "3K", "2K"}},
+            {1600, new String[]{"2K", "2K", "2K", "2K", "1K", "2K", "1K"}},
+            {1100, new String[]{"1K", "1K", "1K", "2K", "1K", "2K", "1K"}},
+            {1000, new String[]{"1K", "1K", "1K", "1K", "1K", "1K", "1K"}},
+            {-1000, new String[]{"-1K", "-1K", "-1K", "-1K", "-1K", "-1K", "-1K"}},
+            {-1100, new String[]{"-1K", "-1K", "-1K", "-2K", "-1K", "-1K", "-2K"}},
+            {-1600, new String[]{"-2K", "-2K", "-2K", "-2K", "-1K", "-1K", "-2K"}},
+            {-2500, new String[]{"-2K", "-3K", "-2K", "-3K", "-2K", "-2K", "-3K"}},
+            {-5500, new String[]{"-6K", "-6K", "-5K", "-6K", "-5K", "-5K", "-6K"}},
+            {5501, new String[]{"6K", "6K", "6K", "6K", "5K", "6K", "5K"}},
+            {-5501, new String[]{"-6K", "-6K", "-6K", "-6K", "-5K", "-5K", "-6K"}},
+            {1001, new String[]{"1K", "1K", "1K", "2K", "1K", "2K", "1K"}},
+            {-1001, new String[]{"-1K", "-1K", "-1K", "-2K", "-1K", "-1K", "-2K"}},
+            {4501, new String[]{"5K", "5K", "5K", "5K", "4K", "5K", "4K"}},
+            {-4501, new String[]{"-5K", "-5K", "-5K", "-5K", "-4K", "-4K", "-5K"}},
+            {4500, new String[]{"4K", "5K", "4K", "5K", "4K", "5K", "4K"}},
+            {-4500, new String[]{"-4K", "-5K", "-4K", "-5K", "-4K", "-4K", "-5K"}},};
+    }
+
+    @DataProvider(name = "roundingFract")
+    Object[][] roundingFract() {
+        return new Object[][]{
+            // Number, half_even, half_up, half_down, up, down, ceiling, floor
+            {5550, new String[]{"5.5K", "5.5K", "5.5K", "5.6K", "5.5K", "5.6K", "5.5K"}},
+            {2550, new String[]{"2.5K", "2.5K", "2.5K", "2.6K", "2.5K", "2.6K", "2.5K"}},
+            {1660, new String[]{"1.7K", "1.7K", "1.7K", "1.7K", "1.6K", "1.7K", "1.6K"}},
+            {1110, new String[]{"1.1K", "1.1K", "1.1K", "1.2K", "1.1K", "1.2K", "1.1K"}},
+            {1000, new String[]{"1.0K", "1.0K", "1.0K", "1.0K", "1.0K", "1.0K", "1.0K"}},
+            {-1000, new String[]{"-1.0K", "-1.0K", "-1.0K", "-1.0K", "-1.0K", "-1.0K", "-1.0K"}},
+            {-1110, new String[]{"-1.1K", "-1.1K", "-1.1K", "-1.2K", "-1.1K", "-1.1K", "-1.2K"}},
+            {-1660, new String[]{"-1.7K", "-1.7K", "-1.7K", "-1.7K", "-1.6K", "-1.6K", "-1.7K"}},
+            {-2550, new String[]{"-2.5K", "-2.5K", "-2.5K", "-2.6K", "-2.5K", "-2.5K", "-2.6K"}},
+            {-5550, new String[]{"-5.5K", "-5.5K", "-5.5K", "-5.6K", "-5.5K", "-5.5K", "-5.6K"}},
+            {5551, new String[]{"5.6K", "5.6K", "5.6K", "5.6K", "5.5K", "5.6K", "5.5K"}},
+            {-5551, new String[]{"-5.6K", "-5.6K", "-5.6K", "-5.6K", "-5.5K", "-5.5K", "-5.6K"}},
+            {1001, new String[]{"1.0K", "1.0K", "1.0K", "1.1K", "1.0K", "1.1K", "1.0K"}},
+            {-1001, new String[]{"-1.0K", "-1.0K", "-1.0K", "-1.1K", "-1.0K", "-1.0K", "-1.1K"}},
+            {4551, new String[]{"4.6K", "4.6K", "4.6K", "4.6K", "4.5K", "4.6K", "4.5K"}},
+            {-4551, new String[]{"-4.6K", "-4.6K", "-4.6K", "-4.6K", "-4.5K", "-4.5K", "-4.6K"}},
+            {4500, new String[]{"4.5K", "4.5K", "4.5K", "4.5K", "4.5K", "4.5K", "4.5K"}},
+            {-4500, new String[]{"-4.5K", "-4.5K", "-4.5K", "-4.5K", "-4.5K", "-4.5K", "-4.5K"}},};
+    }
+
+    @DataProvider(name = "rounding2Fract")
+    Object[][] rounding2Fract() {
+        return new Object[][]{
+            // Number, half_even, half_up, half_down
+            {1115, new String[]{"1.11K", "1.11K", "1.11K"}},
+            {1125, new String[]{"1.12K", "1.13K", "1.12K"}},
+            {1135, new String[]{"1.14K", "1.14K", "1.14K"}},
+            {3115, new String[]{"3.12K", "3.12K", "3.12K"}},
+            {3125, new String[]{"3.12K", "3.13K", "3.12K"}},
+            {3135, new String[]{"3.13K", "3.13K", "3.13K"}},
+            {6865, new String[]{"6.87K", "6.87K", "6.87K"}},
+            {6875, new String[]{"6.88K", "6.88K", "6.87K"}},
+            {6885, new String[]{"6.88K", "6.88K", "6.88K"}},
+            {3124, new String[]{"3.12K", "3.12K", "3.12K"}},
+            {3126, new String[]{"3.13K", "3.13K", "3.13K"}},
+            {3128, new String[]{"3.13K", "3.13K", "3.13K"}},
+            {6864, new String[]{"6.86K", "6.86K", "6.86K"}},
+            {6865, new String[]{"6.87K", "6.87K", "6.87K"}},
+            {6868, new String[]{"6.87K", "6.87K", "6.87K"}},
+            {4685, new String[]{"4.68K", "4.68K", "4.68K"}},
+            {4687, new String[]{"4.69K", "4.69K", "4.69K"}},
+            {4686, new String[]{"4.69K", "4.69K", "4.69K"}},};
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void testNullMode() {
+        NumberFormat fmt = NumberFormat
+                .getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
+        fmt.setRoundingMode(null);
+    }
+
+    @Test
+    public void testDefaultRoundingMode() {
+        NumberFormat fmt = NumberFormat
+                .getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
+        assertEquals(fmt.getRoundingMode(), RoundingMode.HALF_EVEN,
+                "Default RoundingMode should be " + RoundingMode.HALF_EVEN);
+    }
+
+    @Test(dataProvider = "roundingData")
+    public void testRounding(Object number, String[] expected) {
+        for (int index = 0; index < MODES.size(); index++) {
+            testRoundingMode(number, expected[index], 0, MODES.get(index));
+        }
+    }
+
+    @Test(dataProvider = "roundingFract")
+    public void testRoundingFract(Object number, String[] expected) {
+        for (int index = 0; index < MODES.size(); index++) {
+            testRoundingMode(number, expected[index], 1, MODES.get(index));
+        }
+    }
+
+    @Test(dataProvider = "rounding2Fract")
+    public void testRounding2Fract(Object number, String[] expected) {
+        List<RoundingMode> rModes = List.of(RoundingMode.HALF_EVEN,
+                RoundingMode.HALF_UP, RoundingMode.HALF_DOWN);
+        for (int index = 0; index < rModes.size(); index++) {
+            testRoundingMode(number, expected[index], 2, rModes.get(index));
+        }
+    }
+
+    private void testRoundingMode(Object number, String expected,
+            int fraction, RoundingMode rounding) {
+        NumberFormat fmt = NumberFormat
+                .getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
+        fmt.setRoundingMode(rounding);
+        assertEquals(fmt.getRoundingMode(), rounding,
+                "RoundingMode set is not returned by getRoundingMode");
+
+        fmt.setMinimumFractionDigits(fraction);
+        String result = fmt.format(number);
+        assertEquals(result, expected, "Incorrect formatting of number "
+                + number + " using rounding mode: " + rounding);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/TestCompactNumber.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,589 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8177552
+ * @summary Checks the functioning of compact number format
+ * @modules jdk.localedata
+ * @run testng/othervm TestCompactNumber
+ */
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.Locale;
+import java.util.stream.Stream;
+import static org.testng.Assert.*;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class TestCompactNumber {
+
+    private static final NumberFormat FORMAT_DZ_LONG = NumberFormat
+            .getCompactNumberInstance(new Locale("dz"), NumberFormat.Style.LONG);
+
+    private static final NumberFormat FORMAT_EN_US_SHORT = NumberFormat
+            .getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
+
+    private static final NumberFormat FORMAT_EN_LONG = NumberFormat
+            .getCompactNumberInstance(new Locale("en"), NumberFormat.Style.LONG);
+
+    private static final NumberFormat FORMAT_HI_IN_LONG = NumberFormat
+            .getCompactNumberInstance(new Locale("hi", "IN"), NumberFormat.Style.LONG);
+
+    private static final NumberFormat FORMAT_JA_JP_SHORT = NumberFormat
+            .getCompactNumberInstance(Locale.JAPAN, NumberFormat.Style.SHORT);
+
+    private static final NumberFormat FORMAT_IT_SHORT = NumberFormat
+            .getCompactNumberInstance(new Locale("it"), NumberFormat.Style.SHORT);
+
+    private static final NumberFormat FORMAT_CA_LONG = NumberFormat
+            .getCompactNumberInstance(new Locale("ca"), NumberFormat.Style.LONG);
+
+    private static final NumberFormat FORMAT_AS_LONG = NumberFormat
+            .getCompactNumberInstance(new Locale("as"), NumberFormat.Style.LONG);
+
+    private static final NumberFormat FORMAT_BRX_SHORT = NumberFormat
+            .getCompactNumberInstance(new Locale("brx"), NumberFormat.Style.SHORT);
+
+    private static final NumberFormat FORMAT_SW_LONG = NumberFormat
+            .getCompactNumberInstance(new Locale("sw"), NumberFormat.Style.LONG);
+
+    private static final NumberFormat FORMAT_SE_SHORT = NumberFormat
+            .getCompactNumberInstance(new Locale("se"), NumberFormat.Style.SHORT);
+
+    @DataProvider(name = "format")
+    Object[][] compactFormatData() {
+        return new Object[][]{
+            // compact number format instance, number to format, formatted output
+            {FORMAT_DZ_LONG, 1000.09, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55"
+                + "\u0FB2\u0F42 \u0F21"},
+            {FORMAT_DZ_LONG, -999.99, "-\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55"
+                + "\u0FB2\u0F42 \u0F21"},
+            {FORMAT_DZ_LONG, -0.0, "-\u0F20"},
+            {FORMAT_DZ_LONG, 3000L, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55"
+                + "\u0FB2\u0F42 \u0F23"},
+            {FORMAT_DZ_LONG, new BigInteger("12345678901234567890"), "\u0F51"
+                + "\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F66"
+                + "\u0F0B\u0F61\u0F0B \u0F21\u0F22\u0F23\u0F24\u0F25\u0F27"},
+            // negative
+            {FORMAT_DZ_LONG, new BigInteger("-12345678901234567890"), "-\u0F51"
+                + "\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F66"
+                + "\u0F0B\u0F61\u0F0B \u0F21\u0F22\u0F23\u0F24\u0F25\u0F27"},
+            {FORMAT_DZ_LONG, new BigDecimal("12345678901234567890.89"), "\u0F51"
+                + "\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F66"
+                + "\u0F0B\u0F61\u0F0B \u0F21\u0F22\u0F23\u0F24\u0F25\u0F27"},
+            {FORMAT_DZ_LONG, new BigDecimal("-12345678901234567890.89"), "-\u0F51"
+                + "\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F66"
+                + "\u0F0B\u0F61\u0F0B \u0F21\u0F22\u0F23\u0F24\u0F25\u0F27"},
+            // Zeros
+            {FORMAT_EN_US_SHORT, 0, "0"},
+            {FORMAT_EN_US_SHORT, 0.0, "0"},
+            {FORMAT_EN_US_SHORT, -0.0, "-0"},
+            // Less than 1000 no suffix
+            {FORMAT_EN_US_SHORT, 499, "499"},
+            // Boundary number
+            {FORMAT_EN_US_SHORT, 1000.0, "1K"},
+            // Long
+            {FORMAT_EN_US_SHORT, 3000L, "3K"},
+            {FORMAT_EN_US_SHORT, 30000L, "30K"},
+            {FORMAT_EN_US_SHORT, 300000L, "300K"},
+            {FORMAT_EN_US_SHORT, 3000000L, "3M"},
+            {FORMAT_EN_US_SHORT, 30000000L, "30M"},
+            {FORMAT_EN_US_SHORT, 300000000L, "300M"},
+            {FORMAT_EN_US_SHORT, 3000000000L, "3B"},
+            {FORMAT_EN_US_SHORT, 30000000000L, "30B"},
+            {FORMAT_EN_US_SHORT, 300000000000L, "300B"},
+            {FORMAT_EN_US_SHORT, 3000000000000L, "3T"},
+            {FORMAT_EN_US_SHORT, 30000000000000L, "30T"},
+            {FORMAT_EN_US_SHORT, 300000000000000L, "300T"},
+            {FORMAT_EN_US_SHORT, 3000000000000000L, "3000T"},
+            // Negatives
+            {FORMAT_EN_US_SHORT, -3000L, "-3K"},
+            {FORMAT_EN_US_SHORT, -30000L, "-30K"},
+            {FORMAT_EN_US_SHORT, -300000L, "-300K"},
+            {FORMAT_EN_US_SHORT, -3000000L, "-3M"},
+            {FORMAT_EN_US_SHORT, -30000000L, "-30M"},
+            {FORMAT_EN_US_SHORT, -300000000L, "-300M"},
+            {FORMAT_EN_US_SHORT, -3000000000L, "-3B"},
+            {FORMAT_EN_US_SHORT, -30000000000L, "-30B"},
+            {FORMAT_EN_US_SHORT, -300000000000L, "-300B"},
+            {FORMAT_EN_US_SHORT, -3000000000000L, "-3T"},
+            {FORMAT_EN_US_SHORT, -30000000000000L, "-30T"},
+            {FORMAT_EN_US_SHORT, -300000000000000L, "-300T"},
+            {FORMAT_EN_US_SHORT, -3000000000000000L, "-3000T"},
+            // Double
+            {FORMAT_EN_US_SHORT, 3000.0, "3K"},
+            {FORMAT_EN_US_SHORT, 30000.0, "30K"},
+            {FORMAT_EN_US_SHORT, 300000.0, "300K"},
+            {FORMAT_EN_US_SHORT, 3000000.0, "3M"},
+            {FORMAT_EN_US_SHORT, 30000000.0, "30M"},
+            {FORMAT_EN_US_SHORT, 300000000.0, "300M"},
+            {FORMAT_EN_US_SHORT, 3000000000.0, "3B"},
+            {FORMAT_EN_US_SHORT, 30000000000.0, "30B"},
+            {FORMAT_EN_US_SHORT, 300000000000.0, "300B"},
+            {FORMAT_EN_US_SHORT, 3000000000000.0, "3T"},
+            {FORMAT_EN_US_SHORT, 30000000000000.0, "30T"},
+            {FORMAT_EN_US_SHORT, 300000000000000.0, "300T"},
+            {FORMAT_EN_US_SHORT, 3000000000000000.0, "3000T"},
+            // Negatives
+            {FORMAT_EN_US_SHORT, -3000.0, "-3K"},
+            {FORMAT_EN_US_SHORT, -30000.0, "-30K"},
+            {FORMAT_EN_US_SHORT, -300000.0, "-300K"},
+            {FORMAT_EN_US_SHORT, -3000000.0, "-3M"},
+            {FORMAT_EN_US_SHORT, -30000000.0, "-30M"},
+            {FORMAT_EN_US_SHORT, -300000000.0, "-300M"},
+            {FORMAT_EN_US_SHORT, -3000000000.0, "-3B"},
+            {FORMAT_EN_US_SHORT, -30000000000.0, "-30B"},
+            {FORMAT_EN_US_SHORT, -300000000000.0, "-300B"},
+            {FORMAT_EN_US_SHORT, -3000000000000.0, "-3T"},
+            {FORMAT_EN_US_SHORT, -30000000000000.0, "-30T"},
+            {FORMAT_EN_US_SHORT, -300000000000000.0, "-300T"},
+            {FORMAT_EN_US_SHORT, -3000000000000000.0, "-3000T"},
+            // BigInteger
+            {FORMAT_EN_US_SHORT, new BigInteger("12345678901234567890"),
+                "12345679T"},
+            {FORMAT_EN_US_SHORT, new BigInteger("-12345678901234567890"),
+                "-12345679T"},
+            //BigDecimal
+            {FORMAT_EN_US_SHORT, new BigDecimal("12345678901234567890.89"),
+                "12345679T"},
+            {FORMAT_EN_US_SHORT, new BigDecimal("-12345678901234567890.89"),
+                "-12345679T"},
+            {FORMAT_EN_US_SHORT, new BigDecimal("12345678901234567890123466767.89"),
+                "12345678901234568T"},
+            {FORMAT_EN_US_SHORT, new BigDecimal(
+                "12345678901234567890878732267863209.89"),
+                "12345678901234567890879T"},
+            // number as exponent
+            {FORMAT_EN_US_SHORT, 9.78313E+3, "10K"},
+            // Less than 1000 no suffix
+            {FORMAT_EN_LONG, 999, "999"},
+            // Round the value and then format
+            {FORMAT_EN_LONG, 999.99, "1 thousand"},
+            // 10 thousand
+            {FORMAT_EN_LONG, 99000, "99 thousand"},
+            // Long path
+            {FORMAT_EN_LONG, 330000, "330 thousand"},
+            // Double path
+            {FORMAT_EN_LONG, 3000.90, "3 thousand"},
+            // BigInteger path
+            {FORMAT_EN_LONG, new BigInteger("12345678901234567890"),
+                "12345679 trillion"},
+            //BigDecimal path
+            {FORMAT_EN_LONG, new BigDecimal("12345678901234567890.89"),
+                "12345679 trillion"},
+            // Less than 1000 no suffix
+            {FORMAT_HI_IN_LONG, -999, "-999"},
+            // Round the value with 0 fraction digits and format it
+            {FORMAT_HI_IN_LONG, -999.99, "-1 \u0939\u091C\u093C\u093E\u0930"},
+            // 10 thousand
+            {FORMAT_HI_IN_LONG, 99000, "99 \u0939\u091C\u093C\u093E\u0930"},
+            // Long path
+            {FORMAT_HI_IN_LONG, 330000, "3 \u0932\u093E\u0916"},
+            // Double path
+            {FORMAT_HI_IN_LONG, 3000.90, "3 \u0939\u091C\u093C\u093E\u0930"},
+            // BigInteger path
+            {FORMAT_HI_IN_LONG, new BigInteger("12345678901234567890"),
+                "123456789 \u0916\u0930\u092C"},
+            // BigDecimal path
+            {FORMAT_HI_IN_LONG, new BigDecimal("12345678901234567890.89"),
+                "123456789 \u0916\u0930\u092C"},
+            // 1000 does not have any suffix in "ja" locale
+            {FORMAT_JA_JP_SHORT, -999.99, "-1,000"},
+            // 0-9999 does not have any suffix
+            {FORMAT_JA_JP_SHORT, 9999, "9,999"},
+            // 99000/10000 => 9.9\u4E07 rounded to 10\u4E07
+            {FORMAT_JA_JP_SHORT, 99000, "10\u4E07"},
+            // Negative
+            {FORMAT_JA_JP_SHORT, -99000, "-10\u4E07"},
+            // Long path
+            {FORMAT_JA_JP_SHORT, 330000, "33\u4E07"},
+            // Double path
+            {FORMAT_JA_JP_SHORT, 3000.90, "3,001"},
+            // BigInteger path
+            {FORMAT_JA_JP_SHORT, new BigInteger("12345678901234567890"),
+                "12345679\u5146"},
+            // BigDecimal path
+            {FORMAT_JA_JP_SHORT, new BigDecimal("12345678901234567890.89"),
+                "12345679\u5146"},
+            // less than 1000 no suffix
+            {FORMAT_IT_SHORT, 499, "499"},
+            // Boundary number
+            {FORMAT_IT_SHORT, 1000, "1.000"},
+            // Long path
+            {FORMAT_IT_SHORT, 3000000L, "3\u00a0Mln"},
+            // Double path
+            {FORMAT_IT_SHORT, 3000000.0, "3\u00a0Mln"},
+            // BigInteger path
+            {FORMAT_IT_SHORT, new BigInteger("12345678901234567890"),
+                "12345679\u00a0Bln"},
+            // BigDecimal path
+            {FORMAT_IT_SHORT, new BigDecimal("12345678901234567890.89"),
+                "12345679\u00a0Bln"},
+            {FORMAT_CA_LONG, 999, "999"},
+            {FORMAT_CA_LONG, 999.99, "1 miler"},
+            {FORMAT_CA_LONG, 99000, "99 milers"},
+            {FORMAT_CA_LONG, 330000, "330 milers"},
+            {FORMAT_CA_LONG, 3000.90, "3 miler"},
+            {FORMAT_CA_LONG, 1000000, "1 mili\u00f3"},
+            {FORMAT_CA_LONG, new BigInteger("12345678901234567890"),
+                "12345679 bilions"},
+            {FORMAT_CA_LONG, new BigDecimal("12345678901234567890.89"),
+                "12345679 bilions"},
+            {FORMAT_AS_LONG, 5000.0, "\u09eb \u09b9\u09be\u099c\u09be\u09f0"},
+            {FORMAT_AS_LONG, 50000.0, "\u09eb\u09e6 \u09b9\u09be\u099c\u09be\u09f0"},
+            {FORMAT_AS_LONG, 500000.0, "\u09eb \u09b2\u09be\u0996"},
+            {FORMAT_AS_LONG, 5000000.0, "\u09eb \u09a8\u09bf\u09af\u09c1\u09a4"},
+            {FORMAT_AS_LONG, 50000000.0, "\u09eb\u09e6 \u09a8\u09bf\u09af\u09c1\u09a4"},
+            {FORMAT_AS_LONG, 500000000.0, "\u09eb\u09e6\u09e6 \u09a8\u09bf\u09af\u09c1\u09a4"},
+            {FORMAT_AS_LONG, 5000000000.0, "\u09eb \u09b6\u09a4 \u0995\u09cb\u099f\u09bf"},
+            {FORMAT_AS_LONG, 50000000000.0, "\u09eb\u09e6 \u09b6\u09a4 \u0995\u09cb\u099f\u09bf"},
+            {FORMAT_AS_LONG, 500000000000.0, "\u09eb\u09e6\u09e6 \u09b6\u09a4 \u0995\u09cb\u099f\u09bf"},
+            {FORMAT_AS_LONG, 5000000000000.0, "\u09eb \u09b6\u09a4 \u09aa\u09f0\u09be\u09f0\u09cd\u09a6\u09cd\u09a7"},
+            {FORMAT_AS_LONG, 50000000000000.0, "\u09eb\u09e6 \u09b6\u09a4 \u09aa\u09f0\u09be\u09f0\u09cd\u09a6\u09cd\u09a7"},
+            {FORMAT_AS_LONG, 500000000000000.0, "\u09eb\u09e6\u09e6 \u09b6\u09a4 \u09aa\u09f0\u09be\u09f0\u09cd\u09a6\u09cd\u09a7"},
+            {FORMAT_AS_LONG, 5000000000000000.0, "\u09eb\u09e6\u09e6\u09e6 \u09b6\u09a4 \u09aa\u09f0\u09be\u09f0\u09cd\u09a6\u09cd\u09a7"},
+            {FORMAT_AS_LONG, new BigInteger("12345678901234567890"),
+                "\u09e7\u09e8\u09e9\u09ea\u09eb\u09ec\u09ed\u09ef \u09b6\u09a4 \u09aa\u09f0\u09be\u09f0\u09cd\u09a6\u09cd\u09a7"},
+            {FORMAT_AS_LONG, new BigDecimal("12345678901234567890123466767.89"),
+                "\u09e7\u09e8\u09e9\u09ea\u09eb\u09ec\u09ed\u09ee\u09ef\u09e6\u09e7\u09e8\u09e9\u09ea\u09eb\u09ec\u09ee \u09b6\u09a4 \u09aa\u09f0\u09be\u09f0\u09cd\u09a6\u09cd\u09a7"},
+            {FORMAT_BRX_SHORT, 999, "999"},
+            {FORMAT_BRX_SHORT, 999.99, "1K"},
+            {FORMAT_BRX_SHORT, 99000, "99K"},
+            {FORMAT_BRX_SHORT, 330000, "330K"},
+            {FORMAT_BRX_SHORT, 3000.90, "3K"},
+            {FORMAT_BRX_SHORT, 1000000, "1M"},
+            {FORMAT_BRX_SHORT, new BigInteger("12345678901234567890"),
+                    "12345679T"},
+            {FORMAT_BRX_SHORT, new BigDecimal("12345678901234567890.89"),
+                    "12345679T"},
+            // Less than 1000 no suffix
+            {FORMAT_SW_LONG, 499, "499"},
+            // Boundary number
+            {FORMAT_SW_LONG, 1000, "elfu 1"},
+            // Long path
+            {FORMAT_SW_LONG, 3000000L, "milioni 3"},
+            // Long path, negative
+            {FORMAT_SW_LONG, -3000000L, "milioni -3"},
+            // Double path
+            {FORMAT_SW_LONG, 3000000.0, "milioni 3"},
+            // Double path, negative
+            {FORMAT_SW_LONG, -3000000.0, "milioni -3"},
+            // BigInteger path
+            {FORMAT_SW_LONG, new BigInteger("12345678901234567890"),
+                "trilioni 12345679"},
+            // BigDecimal path
+            {FORMAT_SW_LONG, new BigDecimal("12345678901234567890.89"),
+                "trilioni 12345679"},
+            // Positives
+            // No compact form
+            {FORMAT_SE_SHORT, 999, "999"},
+            // Long
+            {FORMAT_SE_SHORT, 8000000L, "8\u00a0mn"},
+            // Double
+            {FORMAT_SE_SHORT, 8000.98, "8\u00a0dt"},
+            // Big integer
+            {FORMAT_SE_SHORT, new BigInteger("12345678901234567890"), "12345679\u00a0bn"},
+            // Big decimal
+            {FORMAT_SE_SHORT, new BigDecimal("12345678901234567890.98"), "12345679\u00a0bn"},
+            // Negatives
+            // No compact form
+            {FORMAT_SE_SHORT, -999, "\u2212999"},
+            // Long
+            {FORMAT_SE_SHORT, -8000000L, "\u22128\u00a0mn"},
+            // Double
+            {FORMAT_SE_SHORT, -8000.98, "\u22128\u00a0dt"},
+            // BigInteger
+            {FORMAT_SE_SHORT, new BigInteger("-12345678901234567890"), "\u221212345679\u00a0bn"},
+            // BigDecimal
+            {FORMAT_SE_SHORT, new BigDecimal("-12345678901234567890.98"), "\u221212345679\u00a0bn"},};
+    }
+
+    @DataProvider(name = "parse")
+    Object[][] compactParseData() {
+        return new Object[][]{
+                // compact number format instance, string to parse, parsed number, return type
+                {FORMAT_DZ_LONG, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2"
+                        + "\u0F42 \u0F21", 1000L, Long.class},
+                {FORMAT_DZ_LONG, "-\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2"
+                        + "\u0F42 \u0F23", -3000L, Long.class},
+                {FORMAT_DZ_LONG, "\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62"
+                        + "\u0F0B\u0F66\u0F0B\u0F61\u0F0B \u0F21"
+                        + "\u0F22\u0F23\u0F24\u0F25\u0F27", 1.23457E19, Double.class},
+                {FORMAT_DZ_LONG, "-\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62"
+                        + "\u0F0B\u0F66\u0F0B\u0F61\u0F0B \u0F21"
+                        + "\u0F22\u0F23\u0F24\u0F25\u0F27", -1.23457E19, Double.class},
+                {FORMAT_EN_US_SHORT, "-0.0", -0.0, Double.class},
+                {FORMAT_EN_US_SHORT, "-0", -0.0, Double.class},
+                {FORMAT_EN_US_SHORT, "0", 0L, Long.class},
+                {FORMAT_EN_US_SHORT, "499", 499L, Long.class},
+                {FORMAT_EN_US_SHORT, "-499", -499L, Long.class},
+                {FORMAT_EN_US_SHORT, "499.89", 499.89, Double.class},
+                {FORMAT_EN_US_SHORT, "-499.89", -499.89, Double.class},
+                {FORMAT_EN_US_SHORT, "1K", 1000L, Long.class},
+                {FORMAT_EN_US_SHORT, "-1K", -1000L, Long.class},
+                {FORMAT_EN_US_SHORT, "3K", 3000L, Long.class},
+                {FORMAT_EN_US_SHORT, "17K", 17000L, Long.class},
+                {FORMAT_EN_US_SHORT, "-17K", -17000L, Long.class},
+                {FORMAT_EN_US_SHORT, "-3K", -3000L, Long.class},
+                {FORMAT_EN_US_SHORT, "12345678901234567890", 1.2345678901234567E19, Double.class},
+                {FORMAT_EN_US_SHORT, "12345679T", 1.2345679E19, Double.class},
+                {FORMAT_EN_US_SHORT, "-12345679T", -1.2345679E19, Double.class},
+                {FORMAT_EN_US_SHORT, "599.01K", 599010L, Long.class},
+                {FORMAT_EN_US_SHORT, "-599.01K", -599010L, Long.class},
+                {FORMAT_EN_US_SHORT, "599444444.90T", 5.994444449E20, Double.class},
+                {FORMAT_EN_US_SHORT, "-599444444.90T", -5.994444449E20, Double.class},
+                {FORMAT_EN_US_SHORT, "123456789012345.5678K", 123456789012345568L, Long.class},
+                {FORMAT_EN_US_SHORT, "17.000K", 17000L, Long.class},
+                {FORMAT_EN_US_SHORT, "123.56678K", 123566.78000, Double.class},
+                {FORMAT_EN_US_SHORT, "-123.56678K", -123566.78000, Double.class},
+                {FORMAT_EN_LONG, "999", 999L, Long.class},
+                {FORMAT_EN_LONG, "1 thousand", 1000L, Long.class},
+                {FORMAT_EN_LONG, "3 thousand", 3000L, Long.class},
+                {FORMAT_EN_LONG, "12345679 trillion", 1.2345679E19, Double.class},
+                {FORMAT_HI_IN_LONG, "999", 999L, Long.class},
+                {FORMAT_HI_IN_LONG, "-999", -999L, Long.class},
+                {FORMAT_HI_IN_LONG, "1 \u0939\u091C\u093C\u093E\u0930", 1000L, Long.class},
+                {FORMAT_HI_IN_LONG, "-1 \u0939\u091C\u093C\u093E\u0930", -1000L, Long.class},
+                {FORMAT_HI_IN_LONG, "3 \u0939\u091C\u093C\u093E\u0930", 3000L, Long.class},
+                {FORMAT_HI_IN_LONG, "12345679 \u0916\u0930\u092C", 1234567900000000000L, Long.class},
+                {FORMAT_HI_IN_LONG, "-12345679 \u0916\u0930\u092C", -1234567900000000000L, Long.class},
+                {FORMAT_JA_JP_SHORT, "-99", -99L, Long.class},
+                {FORMAT_JA_JP_SHORT, "1\u4E07", 10000L, Long.class},
+                {FORMAT_JA_JP_SHORT, "30\u4E07", 300000L, Long.class},
+                {FORMAT_JA_JP_SHORT, "-30\u4E07", -300000L, Long.class},
+                {FORMAT_JA_JP_SHORT, "12345679\u5146", 1.2345679E19, Double.class},
+                {FORMAT_JA_JP_SHORT, "-12345679\u5146", -1.2345679E19, Double.class},
+                {FORMAT_IT_SHORT, "-99", -99L, Long.class},
+                {FORMAT_IT_SHORT, "1\u00a0Mln", 1000000L, Long.class},
+                {FORMAT_IT_SHORT, "30\u00a0Mln", 30000000L, Long.class},
+                {FORMAT_IT_SHORT, "-30\u00a0Mln", -30000000L, Long.class},
+                {FORMAT_IT_SHORT, "12345679\u00a0Bln", 1.2345679E19, Double.class},
+                {FORMAT_IT_SHORT, "-12345679\u00a0Bln", -1.2345679E19, Double.class},
+                {FORMAT_SW_LONG, "-0.0", -0.0, Double.class},
+                {FORMAT_SW_LONG, "499", 499L, Long.class},
+                {FORMAT_SW_LONG, "elfu 1", 1000L, Long.class},
+                {FORMAT_SW_LONG, "elfu 3", 3000L, Long.class},
+                {FORMAT_SW_LONG, "elfu 17", 17000L, Long.class},
+                {FORMAT_SW_LONG, "elfu -3", -3000L, Long.class},
+                {FORMAT_SW_LONG, "499", 499L, Long.class},
+                {FORMAT_SW_LONG, "-499", -499L, Long.class},
+                {FORMAT_SW_LONG, "elfu 1", 1000L, Long.class},
+                {FORMAT_SW_LONG, "elfu 3", 3000L, Long.class},
+                {FORMAT_SW_LONG, "elfu -3", -3000L, Long.class},
+                {FORMAT_SW_LONG, "elfu 17", 17000L, Long.class},
+                {FORMAT_SW_LONG, "trilioni 12345679", 1.2345679E19, Double.class},
+                {FORMAT_SW_LONG, "trilioni -12345679", -1.2345679E19, Double.class},
+                {FORMAT_SW_LONG, "elfu 599.01", 599010L, Long.class},
+                {FORMAT_SW_LONG, "elfu -599.01", -599010L, Long.class},
+                {FORMAT_SE_SHORT, "999", 999L, Long.class},
+                {FORMAT_SE_SHORT, "8\u00a0mn", 8000000L, Long.class},
+                {FORMAT_SE_SHORT, "8\u00a0dt", 8000L, Long.class},
+                {FORMAT_SE_SHORT, "12345679\u00a0bn", 1.2345679E19, Double.class},
+                {FORMAT_SE_SHORT, "12345679,89\u00a0bn", 1.2345679890000001E19, Double.class},
+                {FORMAT_SE_SHORT, "\u2212999", -999L, Long.class},
+                {FORMAT_SE_SHORT, "\u22128\u00a0mn", -8000000L, Long.class},
+                {FORMAT_SE_SHORT, "\u22128\u00a0dt", -8000L, Long.class},
+                {FORMAT_SE_SHORT, "\u221212345679\u00a0bn", -1.2345679E19, Double.class},
+                {FORMAT_SE_SHORT, "\u221212345679,89\u00a0bn", -1.2345679890000001E19, Double.class},};
+    }
+
+    @DataProvider(name = "exceptionParse")
+    Object[][] exceptionParseData() {
+        return new Object[][]{
+            // compact number instance, string to parse, null (no o/p; must throws exception)
+            // no number
+            {FORMAT_DZ_LONG, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2"
+                + "\u0F42", null},
+            // Invalid prefix
+            {FORMAT_DZ_LONG, "-\u0F66\u0F9F\u0F7C\u0F44,\u0F0B\u0F55\u0FB2"
+                + "\u0F42 \u0F23", null},
+            // Invalid prefix for en_US
+            {FORMAT_EN_US_SHORT, "K12,347", null},
+            // Invalid prefix for ja_JP
+            {FORMAT_JA_JP_SHORT, "\u4E071", null},
+            // Localized minus sign should be used
+            {FORMAT_SE_SHORT, "-8\u00a0mn", null},};
+    }
+
+    @DataProvider(name = "invalidParse")
+    Object[][] invalidParseData() {
+        return new Object[][]{
+            // compact number instance, string to parse, parsed number
+            // Prefix and suffix do not match
+            {FORMAT_DZ_LONG, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2"
+                + "\u0F42 \u0F21 KM", 1000L},
+            // Exponents are unparseable
+            {FORMAT_EN_US_SHORT, "-1.05E4K", -1.05},
+            // Default instance does not allow grouping
+            {FORMAT_EN_US_SHORT, "12,347", 12L},
+            // Take partial suffix "K" as 1000 for en_US_SHORT patterns
+            {FORMAT_EN_US_SHORT, "12KM", 12000L},
+            // Invalid suffix
+            {FORMAT_HI_IN_LONG, "-1 \u00a0\u0915.", -1L},};
+    }
+
+    @DataProvider(name = "fieldPosition")
+    Object[][] formatFieldPositionData() {
+        return new Object[][]{
+            //compact number instance, number to format, field, start position, end position, formatted string
+            {FORMAT_DZ_LONG, -3500, NumberFormat.Field.SIGN, 0, 1, "-\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F24"},
+            {FORMAT_DZ_LONG, 3500, NumberFormat.Field.INTEGER, 9, 10, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F24"},
+            {FORMAT_DZ_LONG, -3500, NumberFormat.Field.INTEGER, 10, 11, "-\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F24"},
+            {FORMAT_DZ_LONG, 999, NumberFormat.Field.INTEGER, 0, 3, "\u0F29\u0F29\u0F29"},
+            {FORMAT_DZ_LONG, -999, NumberFormat.Field.INTEGER, 1, 4, "-\u0F29\u0F29\u0F29"},
+            {FORMAT_DZ_LONG, 3500, NumberFormat.Field.PREFIX, 0, 9, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F24"},
+            {FORMAT_DZ_LONG, -3500, NumberFormat.Field.PREFIX, 0, 10, "-\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F24"},
+            {FORMAT_DZ_LONG, 999, NumberFormat.Field.PREFIX, 0, 0, "\u0F29\u0F29\u0F29"},
+            {FORMAT_EN_US_SHORT, -3500, NumberFormat.Field.SIGN, 0, 1, "-4K"},
+            {FORMAT_EN_US_SHORT, 3500, NumberFormat.Field.INTEGER, 0, 1, "4K"},
+            {FORMAT_EN_US_SHORT, 14900000067L, NumberFormat.Field.INTEGER, 0, 2, "15B"},
+            {FORMAT_EN_US_SHORT, -1000, NumberFormat.Field.PREFIX, 0, 1, "-1K"},
+            {FORMAT_EN_US_SHORT, 3500, NumberFormat.Field.SUFFIX, 1, 2, "4K"},
+            {FORMAT_EN_US_SHORT, 14900000067L, NumberFormat.Field.SUFFIX, 2, 3, "15B"},
+            {FORMAT_EN_LONG, 3500, NumberFormat.Field.INTEGER, 0, 1, "4 thousand"},
+            {FORMAT_EN_LONG, 14900000067L, NumberFormat.Field.INTEGER, 0, 2, "15 billion"},
+            {FORMAT_EN_LONG, 3500, NumberFormat.Field.SUFFIX, 1, 10, "4 thousand"},
+            {FORMAT_EN_LONG, 14900000067L, NumberFormat.Field.SUFFIX, 2, 10, "15 billion"},
+            {FORMAT_JA_JP_SHORT, 14900000067L, NumberFormat.Field.INTEGER, 0, 3, "149\u5104"},
+            {FORMAT_JA_JP_SHORT, -999.99, NumberFormat.Field.INTEGER, 1, 6, "-1,000"},
+            {FORMAT_JA_JP_SHORT, 14900000067L, NumberFormat.Field.SUFFIX, 3, 4, "149\u5104"},
+            {FORMAT_JA_JP_SHORT, -999.99, NumberFormat.Field.SUFFIX, 0, 0, "-1,000"},
+            {FORMAT_JA_JP_SHORT, -999.99, NumberFormat.Field.SIGN, 0, 1, "-1,000"},
+            {FORMAT_HI_IN_LONG, -14900000067L, NumberFormat.Field.SIGN, 0, 1,
+                "-15 \u0905\u0930\u092C"},
+            {FORMAT_HI_IN_LONG, 3500, NumberFormat.Field.INTEGER, 0, 1,
+                "4 \u0939\u091C\u093C\u093E\u0930"},
+            {FORMAT_HI_IN_LONG, 14900000067L, NumberFormat.Field.INTEGER, 0, 2,
+                "15 \u0905\u0930\u092C"},
+            {FORMAT_HI_IN_LONG, 3500, NumberFormat.Field.SUFFIX, 1, 7,
+                "4 \u0939\u091C\u093C\u093E\u0930"},
+            {FORMAT_HI_IN_LONG, 14900000067L, NumberFormat.Field.SUFFIX, 2, 6,
+                "15 \u0905\u0930\u092C"},
+            {FORMAT_SE_SHORT, 8000000L, NumberFormat.Field.SUFFIX, 1, 4, "8\u00a0mn"},
+            {FORMAT_SE_SHORT, 8000.98, NumberFormat.Field.SUFFIX, 1, 4, "8\u00a0dt"},
+            {FORMAT_SE_SHORT, new BigInteger("12345678901234567890"), NumberFormat.Field.SUFFIX, 8, 11, "12345679\u00a0bn"},
+            {FORMAT_SE_SHORT, new BigDecimal("12345678901234567890.98"), NumberFormat.Field.SUFFIX, 8, 11, "12345679\u00a0bn"},
+            {FORMAT_SE_SHORT, -8000000L, NumberFormat.Field.INTEGER, 1, 2, "\u22128\u00a0mn"},
+            {FORMAT_SE_SHORT, -8000.98, NumberFormat.Field.SIGN, 0, 1, "\u22128\u00a0dt"},
+            {FORMAT_SE_SHORT, new BigDecimal("-48982865901234567890.98"), NumberFormat.Field.INTEGER, 1, 9, "\u221248982866\u00a0bn"},};
+    }
+
+    @DataProvider(name = "varParsePosition")
+    Object[][] varParsePosition() {
+        return new Object[][]{
+                // compact number instance, parse string, parsed number,
+                // start position, end position, error index
+                {FORMAT_DZ_LONG, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2"
+                        + "\u0F42 \u0F21 KM", 1000L, 0, 10, -1},
+                // Invalid prefix returns null
+                {FORMAT_DZ_LONG, "Number is: -\u0F66\u0F9F\u0F7C\u0F44,\u0F0B\u0F55\u0FB2"
+                        + "\u0F42 \u0F23", null, 11, 11, 11},
+                // Returns null
+                {FORMAT_DZ_LONG, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2"
+                        + "\u0F42", null, 0, 0, 0},
+                {FORMAT_EN_US_SHORT, "Exponent: -1.05E4K", -1.05, 10, 15, -1},
+                // Default instance does not allow grouping
+                {FORMAT_EN_US_SHORT, "12,347", 12L, 0, 2, -1},
+                // Invalid suffix "KM" for en_US_SHORT patterns
+                {FORMAT_EN_US_SHORT, "12KM", 12000L, 0, 3, -1},
+                // Invalid suffix
+                {FORMAT_HI_IN_LONG, "-1 \u00a0\u0915.", -1L, 0, 2, -1},
+                {FORMAT_EN_LONG, "Number is: 12345679 trillion",
+                        1.2345679E19, 11, 28, -1},
+                {FORMAT_EN_LONG, "Number is: -12345679 trillion",
+                        -1.2345679E19, 11, 29, -1},
+                {FORMAT_EN_LONG, "parse 12 thousand and four", 12000L, 6, 17, -1},};
+    }
+
+    @Test
+    public void testInstanceCreation() {
+        Stream.of(NumberFormat.getAvailableLocales()).forEach(l -> NumberFormat
+                .getCompactNumberInstance(l, NumberFormat.Style.SHORT).format(10000));
+        Stream.of(NumberFormat.getAvailableLocales()).forEach(l -> NumberFormat
+                .getCompactNumberInstance(l, NumberFormat.Style.LONG).format(10000));
+    }
+
+    @Test(dataProvider = "format")
+    public void testFormat(NumberFormat cnf, Object number,
+            String expected) {
+        CompactFormatAndParseHelper.testFormat(cnf, number, expected);
+    }
+
+    @Test(dataProvider = "parse")
+    public void testParse(NumberFormat cnf, String parseString,
+            Number expected, Class<? extends Number> returnType) throws ParseException {
+        CompactFormatAndParseHelper.testParse(cnf, parseString, expected, null, returnType);
+    }
+
+    @Test(dataProvider = "parse")
+    public void testParsePosition(NumberFormat cnf, String parseString,
+            Number expected, Class<? extends Number> returnType) throws ParseException {
+        ParsePosition pos = new ParsePosition(0);
+        CompactFormatAndParseHelper.testParse(cnf, parseString, expected, pos, returnType);
+        assertEquals(pos.getIndex(), parseString.length());
+        assertEquals(pos.getErrorIndex(), -1);
+    }
+
+    @Test(dataProvider = "varParsePosition")
+    public void testVarParsePosition(NumberFormat cnf, String parseString,
+            Number expected, int startPosition, int indexPosition,
+            int errPosition) throws ParseException {
+        ParsePosition pos = new ParsePosition(startPosition);
+        CompactFormatAndParseHelper.testParse(cnf, parseString, expected, pos, null);
+        assertEquals(pos.getIndex(), indexPosition);
+        assertEquals(pos.getErrorIndex(), errPosition);
+    }
+
+    @Test(dataProvider = "exceptionParse", expectedExceptions = ParseException.class)
+    public void throwsParseException(NumberFormat cnf, String parseString,
+            Number expected) throws ParseException {
+        CompactFormatAndParseHelper.testParse(cnf, parseString, expected, null, null);
+    }
+
+    @Test(dataProvider = "invalidParse")
+    public void testInvalidParse(NumberFormat cnf, String parseString,
+            Number expected) throws ParseException {
+        CompactFormatAndParseHelper.testParse(cnf, parseString, expected, null, null);
+    }
+
+    @Test(dataProvider = "fieldPosition")
+    public void testFormatWithFieldPosition(NumberFormat nf,
+            Object number, Format.Field field, int posStartExpected,
+            int posEndExpected, String expected) {
+        FieldPosition pos = new FieldPosition(field);
+        StringBuffer buf = new StringBuffer();
+        StringBuffer result = nf.format(number, buf, pos);
+        assertEquals(result.toString(), expected, "Incorrect formatting of the number '"
+                + number + "'");
+        assertEquals(pos.getBeginIndex(), posStartExpected, "Incorrect start position"
+                + " while formatting the number '" + number + "', for the field " + field);
+        assertEquals(pos.getEndIndex(), posEndExpected, "Incorrect end position"
+                + " while formatting the number '" + number + "', for the field " + field);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/TestCompactPatternsValidity.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8177552
+ * @summary Checks the validity of compact number patterns specified through
+ *          CompactNumberFormat constructor
+ * @run testng/othervm TestCompactPatternsValidity
+ */
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.CompactNumberFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.ParseException;
+import java.util.List;
+import java.util.Locale;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class TestCompactPatternsValidity {
+
+    // Max range 10^4
+    private static final String[] COMPACT_PATTERN1 = new String[]{"0", "0", "0", "0K", "00K"};
+    // Quoted special character '.' as prefix
+    private static final String[] COMPACT_PATTERN2 = new String[]{"0", "'.'K0"};
+    // Quoted special character '.' as suffix
+    private static final String[] COMPACT_PATTERN3 = new String[]{"0", "0", "0", "0K", "00K'.'"};
+    // Containing both prefix and suffix
+    private static final String[] COMPACT_PATTERN4 = new String[]{"", "", "H0H", "0K", "00K", "H0G"};
+    // Differing while specifying prefix and suffix
+    private static final String[] COMPACT_PATTERN5 = new String[]{"", "", "", "0K", "K0"};
+    // Containing both prefix ('.') and suffix (K)
+    private static final String[] COMPACT_PATTERN6 = new String[]{"0", "", "", "'.'0K"};
+    // Quoted special character ',' as suffix
+    private static final String[] COMPACT_PATTERN7 = new String[]{"", "0", "0", "0K','"};
+    // Most commonly used type of compact patterns with 15 elements
+    private static final String[] COMPACT_PATTERN8 = new String[]{"", "", "", "0K", "00K", "000K", "0M",
+        "00M", "000M", "0B", "00B", "000B", "0T", "00T", "000T"};
+    // All empty or special patterns; checking the default formatting behaviour
+    private static final String[] COMPACT_PATTERN9 = new String[]{"", "", "", "0", "0", "", "", "", "", "", "", "", "", "", ""};
+    // Patterns beyond 10^19; divisors beyond long range
+    private static final String[] COMPACT_PATTERN10 = new String[]{"", "", "", "0K", "00K", "000K", "0M", "00M",
+        "000M", "0B", "00B", "000B", "0T", "00T", "000T", "0L", "00L", "000L", "0XL", "00XL"};
+    // Containing positive;negative subpatterns
+    private static final String[] COMPACT_PATTERN11 = new String[]{"", "", "", "elfu 0;elfu -0", "elfu 00;elfu -00",
+        "elfu 000;elfu -000", "milioni 0;milioni -0", "milioni 00;milioni -00", "milioni 000;milioni -000"};
+    // Containing both prefix and suffix and positive;negative subpatern
+    private static final String[] COMPACT_PATTERN12 = new String[]{"", "", "H0H;H-0H", "0K;0K-", "00K;-00K", "H0G;-H0G"};
+
+    @DataProvider(name = "invalidPatterns")
+    Object[][] invalidCompactPatterns() {
+        return new Object[][]{
+            // compact patterns
+            // Pattern containing unquoted special character '.'
+            {new String[]{"", "", "", "0K", "00K."}},
+            // Pattern containing invalid single quote
+            {new String[]{"", "", "", "0 'do", "00K"}},
+            {new String[]{"", "", "", "0K", "00 don't"}},
+            // A non empty pattern containing no 0s (min integer digits)
+            {new String[]{"K", "0K", "00K"}},
+            // 0s (min integer digits) exceeding for the range at index 3
+            {new String[]{"", "", "0K", "00000K"}},};
+    }
+
+    @DataProvider(name = "validPatternsFormat")
+    Object[][] validPatternsFormat() {
+        return new Object[][]{
+            // compact patterns, numbers, expected output
+            {COMPACT_PATTERN1, List.of(200, 1000, 3000, 500000), List.of("200", "1K", "3K", "500K")},
+            {COMPACT_PATTERN2, List.of(1, 20, 3000), List.of("1", ".K2", ".K300")},
+            {COMPACT_PATTERN3, List.of(100.99, 1000, 30000), List.of("101", "1K", "30K.")},
+            {COMPACT_PATTERN4, List.of(0.0, 500, -500, 30000, 5000000), List.of("0", "H5H", "-H5H", "30K", "H50G")},
+            {COMPACT_PATTERN5, List.of(100, 1000, 30000), List.of("100", "1K", "K3")},
+            {COMPACT_PATTERN6, List.of(20.99, 1000, 30000), List.of("21", ".1K", ".30K")},
+            {COMPACT_PATTERN7, List.of(100, 1000, new BigInteger("12345678987654321")), List.of("100", "1K,", "12345678987654K,")},
+            {COMPACT_PATTERN8, List.of(new BigInteger("223565686837667632"), new BigDecimal("12322456774334.89766"), 30000, 3456.78),
+                List.of("223566T", "12T", "30K", "3K")},
+            {COMPACT_PATTERN9, List.of(new BigInteger("223566000000000000"), new BigDecimal("12345678987654567"), 30000, 3000),
+                List.of("223,566,000,000,000,000", "12,345,678,987,654,567", "30,000", "3,000")},
+            {COMPACT_PATTERN10, List.of(new BigInteger("100000000000000000"), new BigInteger("10000000000000000000"), new BigDecimal("555555555555555555555.89766"), 30000),
+                List.of("100L", "10XL", "556XL", "30K")},
+            {COMPACT_PATTERN11, List.of(20.99, -20.99, 1000, -1000, 30000, -30000, new BigInteger("12345678987654321"), new BigInteger("-12345678987654321")),
+                List.of("21", "-21", "elfu 1", "elfu -1", "elfu 30", "elfu -30", "milioni 12345678988", "milioni -12345678988")},
+            {COMPACT_PATTERN12, List.of(0, 500, -500, 30000, -3000, 5000000), List.of("0", "H5H", "H-5H", "30K", "3K-", "H50G")},};
+    }
+
+    @DataProvider(name = "validPatternsParse")
+    Object[][] validPatternsParse() {
+        return new Object[][]{
+            // compact patterns, parse string, expected output
+            {COMPACT_PATTERN1, List.of(".56", "200", ".1K", "3K", "500K"), List.of(0.56, 200L, 100L, 3000L, 500000L)},
+            {COMPACT_PATTERN2, List.of("1", ".K2", ".K300"), List.of(1L, 20L, 3000L)},
+            {COMPACT_PATTERN3, List.of("101", "1K", "30K."), List.of(101L, 1000L, 30000L)},
+            {COMPACT_PATTERN4, List.of("0", "H5H", "-H5H", "30K", "H50G"), List.of(0L, 500L, -500L, 30000L, 5000000L)},
+            {COMPACT_PATTERN5, List.of("100", "1K", "K3"), List.of(100L, 1000L, 30000L)},
+            {COMPACT_PATTERN6, List.of("21", ".1K", ".30K"), List.of(21L, 1000L, 30000L)},
+            {COMPACT_PATTERN7, List.of("100", "1K,", "12345678987654K,"), List.of(100L, 1000L, 12345678987654000L)},
+            {COMPACT_PATTERN8, List.of("223566T", "12T", "30K", "3K"), List.of(223566000000000000L, 12000000000000L, 30000L, 3000L)},
+            {COMPACT_PATTERN10, List.of("1L", "100L", "10XL", "556XL", "30K"), List.of(1000000000000000L, 100000000000000000L, 1.0E19, 5.56E20, 30000L)},
+            {COMPACT_PATTERN11, List.of("21", "-21", "100.90", "-100.90", "elfu 1", "elfu -1", "elfu 30", "elfu -30", "milioni 12345678988", "milioni -12345678988"),
+                List.of(21L, -21L, 100.90, -100.90, 1000L, -1000L, 30000L, -30000L, 12345678988000000L, -12345678988000000L)},
+            {COMPACT_PATTERN12, List.of("0", "H5H", "H-5H", "30K", "30K-", "H50G"), List.of(0L, 500L, -500L, 30000L, -30000L, 5000000L)},};
+    }
+
+    @Test(dataProvider = "invalidPatterns",
+            expectedExceptions = RuntimeException.class)
+    public void testInvalidCompactPatterns(String[] compactPatterns) {
+        new CompactNumberFormat("#,##0.0#", DecimalFormatSymbols
+                .getInstance(Locale.US), compactPatterns);
+    }
+
+    @Test(dataProvider = "validPatternsFormat")
+    public void testValidPatternsFormat(String[] compactPatterns,
+            List<Object> numbers, List<String> expected) {
+        CompactNumberFormat fmt = new CompactNumberFormat("#,##0.0#",
+                DecimalFormatSymbols.getInstance(Locale.US), compactPatterns);
+        for (int index = 0; index < numbers.size(); index++) {
+            CompactFormatAndParseHelper.testFormat(fmt, numbers.get(index),
+                    expected.get(index));
+        }
+    }
+
+    @Test(dataProvider = "validPatternsParse")
+    public void testValidPatternsParse(String[] compactPatterns,
+            List<String> parseString, List<Number> numbers) throws ParseException {
+        CompactNumberFormat fmt = new CompactNumberFormat("#,##0.0#",
+                DecimalFormatSymbols.getInstance(Locale.US), compactPatterns);
+        for (int index = 0; index < parseString.size(); index++) {
+            CompactFormatAndParseHelper.testParse(fmt, parseString.get(index),
+                    numbers.get(index), null, null);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/TestEquality.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8177552
+ * @summary Checks the equals and hashCode method of CompactNumberFormat
+ * @modules jdk.localedata
+ * @run testng/othervm TestEquality
+ *
+ */
+
+import java.text.CompactNumberFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Locale;
+import org.testng.annotations.Test;
+
+public class TestEquality {
+
+    @Test
+    public void testEquality() {
+        CompactNumberFormat cnf1 = (CompactNumberFormat) NumberFormat
+                .getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
+
+        CompactNumberFormat cnf2 = (CompactNumberFormat) NumberFormat
+                .getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
+
+        // A custom compact instance with the same state as
+        // compact number instance of "en_US" locale with SHORT style
+        String decimalPattern = "#,##0.###";
+        String[] compactPatterns = new String[]{"", "", "", "0K", "00K", "000K", "0M", "00M", "000M", "0B", "00B", "000B", "0T", "00T", "000T"};
+        DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(Locale.US);
+        CompactNumberFormat cnf3 = new CompactNumberFormat(decimalPattern, symbols, compactPatterns);
+
+        // A compact instance created with different decimalPattern than cnf3
+        CompactNumberFormat cnf4 = new CompactNumberFormat("#,#0.0#", symbols, compactPatterns);
+
+        // A compact instance created with different format symbols than cnf3
+        CompactNumberFormat cnf5 = new CompactNumberFormat(decimalPattern,
+                DecimalFormatSymbols.getInstance(Locale.JAPAN), compactPatterns);
+
+        // A compact instance created with different compact patterns than cnf3
+        CompactNumberFormat cnf6 = new CompactNumberFormat(decimalPattern,
+                symbols, new String[]{"", "", "", "0K", "00K", "000K"});
+
+        // Checking reflexivity
+        if (!cnf1.equals(cnf1)) {
+            throw new RuntimeException("[testEquality() reflexivity FAILED: The compared"
+                    + " objects must be equal]");
+        }
+
+        // Checking symmetry, checking equality of two same objects
+        if (!cnf1.equals(cnf2) || !cnf2.equals(cnf1)) {
+            throw new RuntimeException("[testEquality() symmetry FAILED: The compared"
+                    + " objects must be equal]");
+        }
+
+        // Checking transitivity, three objects must be equal
+        if (!cnf1.equals(cnf2) || !cnf2.equals(cnf3) || !cnf1.equals(cnf3)) {
+            throw new RuntimeException("[testEquality() transitivity FAILED: The compared"
+                    + " objects must be equal]");
+        }
+
+        // Objects must not be equal as the decimalPattern is different
+        checkEquals(cnf3, cnf4, false, "1st", "different decimal pattern");
+
+        // Objects must not be equal as the format symbols instance is different
+        checkEquals(cnf3, cnf5, false, "2nd", "different format symbols");
+
+        // Objects must not be equal as the compact patters are different
+        checkEquals(cnf3, cnf6, false, "3rd", "different compact patterns");
+
+        // Changing the min integer digits of first object; objects must not
+        // be equal
+        cnf1.setMinimumIntegerDigits(5);
+        checkEquals(cnf1, cnf2, false, "4th", "different min integer digits");
+
+        // Changing the min integer digits of second object; objects must
+        // be equal
+        cnf2.setMinimumIntegerDigits(5);
+        checkEquals(cnf1, cnf2, true, "5th", "");
+
+        // Changing the grouping size of first object; objects must not
+        // be equal
+        cnf1.setGroupingSize(4);
+        checkEquals(cnf1, cnf2, false, "6th", "different grouping size");
+
+        // Changing the grouping size if second object; objects must be equal
+        cnf2.setGroupingSize(4);
+        checkEquals(cnf1, cnf2, true, "7th", "");
+
+        // Changing the parseBigDecimal of first object; objects must not
+        // be equal
+        cnf1.setParseBigDecimal(true);
+        checkEquals(cnf1, cnf2, false, "8th", "different parse big decimal");
+
+    }
+
+    private void checkEquals(CompactNumberFormat cnf1, CompactNumberFormat cnf2,
+            boolean mustEqual, String nthComparison, String message) {
+        if (cnf1.equals(cnf2) != mustEqual) {
+            if (mustEqual) {
+                throw new RuntimeException("[testEquality() " + nthComparison
+                        + " comparison FAILED: The compared objects must be equal]");
+            } else {
+                throw new RuntimeException("[testEquality() " + nthComparison
+                        + " comparison FAILED: The compared objects must"
+                        + " not be equal because of " + message + "]");
+            }
+        }
+    }
+
+    @Test
+    public void testHashCode() {
+        NumberFormat cnf1 = NumberFormat
+                .getCompactNumberInstance(Locale.JAPAN, NumberFormat.Style.SHORT);
+        NumberFormat cnf2 = NumberFormat
+                .getCompactNumberInstance(Locale.JAPAN, NumberFormat.Style.SHORT);
+
+        if (cnf1.hashCode() != cnf2.hashCode()) {
+            throw new RuntimeException("[testHashCode() FAILED: hashCode of the"
+                    + " compared objects must match]");
+        }
+    }
+
+    // Test the property of equals and hashCode i.e. two equal object must
+    // always have the same hashCode
+    @Test
+    public void testEqualsAndHashCode() {
+        NumberFormat cnf1 = NumberFormat
+                .getCompactNumberInstance(new Locale("hi", "IN"), NumberFormat.Style.SHORT);
+        cnf1.setMinimumIntegerDigits(5);
+        NumberFormat cnf2 = NumberFormat
+                .getCompactNumberInstance(new Locale("hi", "IN"), NumberFormat.Style.SHORT);
+        cnf2.setMinimumIntegerDigits(5);
+        if (cnf1.equals(cnf2)) {
+            if (cnf1.hashCode() != cnf2.hashCode()) {
+                throw new RuntimeException("[testEqualsAndHashCode() FAILED: two"
+                        + " equal objects must have same hashCode]");
+            }
+        } else {
+            throw new RuntimeException("[testEqualsAndHashCode() FAILED: The"
+                    + " compared objects must be equal]");
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/TestFormatToCharacterIterator.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8177552
+ * @summary Checks the functioning of
+ *          CompactNumberFormat.formatToCharacterIterator method
+ * @modules jdk.localedata
+ * @run testng/othervm TestFormatToCharacterIterator
+ */
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.AttributedCharacterIterator;
+import java.text.CharacterIterator;
+import java.text.Format;
+import java.text.NumberFormat;
+import java.util.Locale;
+import java.util.Set;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class TestFormatToCharacterIterator {
+
+    private static final NumberFormat FORMAT_DZ = NumberFormat
+            .getCompactNumberInstance(new Locale("dz"),
+                    NumberFormat.Style.LONG);
+
+    private static final NumberFormat FORMAT_EN_US = NumberFormat
+            .getCompactNumberInstance(Locale.US,
+                    NumberFormat.Style.SHORT);
+
+    private static final NumberFormat FORMAT_EN_LONG = NumberFormat
+            .getCompactNumberInstance(new Locale("en"),
+                    NumberFormat.Style.LONG);
+
+    @DataProvider(name = "fieldPositions")
+    Object[][] compactFieldPositionData() {
+        return new Object[][]{
+            // compact format instance, number, resulted string, attributes/fields, attribute positions
+            {FORMAT_DZ, 1000.09, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F21",
+                new Format.Field[]{NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER}, new int[]{0, 9, 9, 10}},
+            {FORMAT_DZ, -999.99, "-\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F21",
+                new Format.Field[]{NumberFormat.Field.SIGN, NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER},
+                new int[]{0, 1, 1, 10, 10, 11}},
+            {FORMAT_DZ, -0.0, "-\u0F20", new Format.Field[]{NumberFormat.Field.SIGN, NumberFormat.Field.INTEGER}, new int[]{0, 1, 1, 2}},
+            {FORMAT_DZ, 3000L, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 \u0F23",
+                new Format.Field[]{NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER}, new int[]{0, 9, 9, 10}},
+            {FORMAT_DZ, new BigInteger("12345678901234567890"),
+                "\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F66\u0F0B\u0F61\u0F0B \u0F21\u0F22\u0F23\u0F24\u0F25\u0F27",
+                new Format.Field[]{NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER}, new int[]{0, 14, 14, 20}},
+            {FORMAT_DZ, new BigDecimal("12345678901234567890.89"),
+                "\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F66\u0F0B\u0F61\u0F0B \u0F21\u0F22\u0F23\u0F24\u0F25\u0F27",
+                new Format.Field[]{NumberFormat.Field.PREFIX, NumberFormat.Field.INTEGER}, new int[]{0, 14, 14, 20}},
+            // Zeros
+            {FORMAT_EN_US, 0, "0", new Format.Field[]{NumberFormat.Field.INTEGER}, new int[]{0, 1}},
+            {FORMAT_EN_US, 0.0, "0", new Format.Field[]{NumberFormat.Field.INTEGER}, new int[]{0, 1}},
+            {FORMAT_EN_US, -0.0, "-0", new Format.Field[]{NumberFormat.Field.SIGN, NumberFormat.Field.INTEGER}, new int[]{0, 1, 1, 2}},
+            // Less than 1000 no suffix
+            {FORMAT_EN_US, 499, "499", new Format.Field[]{NumberFormat.Field.INTEGER}, new int[]{0, 3}},
+            // Boundary number
+            {FORMAT_EN_US, 1000.0, "1K",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
+            // Long
+            {FORMAT_EN_US, 3000L, "3K",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
+            {FORMAT_EN_US, 30000L, "30K",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
+            {FORMAT_EN_US, 300000L, "300K",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
+            {FORMAT_EN_US, 3000000L, "3M",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
+            {FORMAT_EN_US, 30000000L, "30M",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
+            {FORMAT_EN_US, 300000000L, "300M",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
+            {FORMAT_EN_US, 3000000000L, "3B",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
+            {FORMAT_EN_US, 30000000000L, "30B",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
+            {FORMAT_EN_US, 300000000000L, "300B",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
+            {FORMAT_EN_US, 3000000000000L, "3T",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
+            {FORMAT_EN_US, 30000000000000L, "30T",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
+            {FORMAT_EN_US, 300000000000000L, "300T",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
+            {FORMAT_EN_US, 3000000000000000L, "3000T",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 4, 4, 5}},
+            // Double
+            {FORMAT_EN_US, 3000.0, "3K",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 2}},
+            {FORMAT_EN_US, 30000.0, "30K",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
+            {FORMAT_EN_US, 300000.0, "300K",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 4}},
+            {FORMAT_EN_US, 3000000000000000.0, "3000T",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 4, 4, 5}},
+            // BigInteger
+            {FORMAT_EN_US, new BigInteger("12345678901234567890"), "12345679T",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 8, 8, 9}},
+            // BigDecimal
+            {FORMAT_EN_US, new BigDecimal("12345678901234567890.89"), "12345679T",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 8, 8, 9}},
+            // Number as exponent
+            {FORMAT_EN_US, 9.78313E+3, "10K",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 3}},
+            // Less than 1000 no suffix
+            {FORMAT_EN_LONG, 999, "999", new Format.Field[]{NumberFormat.Field.INTEGER}, new int[]{0, 3}},
+            // Round the value and then format
+            {FORMAT_EN_LONG, 999.99, "1 thousand",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 10}},
+            // 10 thousand
+            {FORMAT_EN_LONG, 99000, "99 thousand",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 2, 2, 11}},
+            // Long path
+            {FORMAT_EN_LONG, 330000, "330 thousand",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 3, 3, 12}},
+            // Double path
+            {FORMAT_EN_LONG, 3000.90, "3 thousand",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 1, 1, 10}},
+            // BigInteger path
+            {FORMAT_EN_LONG, new BigInteger("12345678901234567890"), "12345679 trillion",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 8, 8, 17}},
+            // BigDecimal path
+            {FORMAT_EN_LONG, new BigDecimal("12345678901234567890.89"), "12345679 trillion",
+                new Format.Field[]{NumberFormat.Field.INTEGER, NumberFormat.Field.SUFFIX}, new int[]{0, 8, 8, 17}}
+        };
+    }
+
+    @Test(dataProvider = "fieldPositions")
+    public void testFormatToCharacterIterator(NumberFormat fmt, Object number,
+            String expected, Format.Field[] expectedFields, int[] positions) {
+        AttributedCharacterIterator iterator = fmt.formatToCharacterIterator(number);
+        assertEquals(getText(iterator), expected, "Incorrect formatting of the number '"
+                + number + "'");
+
+        iterator.first();
+        // Check start and end index of the formatted string
+        assertEquals(iterator.getBeginIndex(), 0, "Incorrect start index: "
+                + iterator.getBeginIndex() + " of the formatted string: " + expected);
+        assertEquals(iterator.getEndIndex(), expected.length(), "Incorrect end index: "
+                + iterator.getEndIndex() + " of the formatted string: " + expected);
+
+        // Check the attributes returned by the formatToCharacterIterator
+        assertEquals(iterator.getAllAttributeKeys(), Set.of(expectedFields),
+                "Attributes do not match while formatting number: " + number);
+
+        // Check the begin and end index for attributes
+        iterator.first();
+        int currentPosition = 0;
+        do {
+            int start = iterator.getRunStart();
+            int end = iterator.getRunLimit();
+            assertEquals(start, positions[currentPosition],
+                    "Incorrect start position for the attribute(s): "
+                    + iterator.getAttributes().keySet());
+            assertEquals(end, positions[currentPosition + 1],
+                    "Incorrect end position for the attribute(s): "
+                    + iterator.getAttributes().keySet());
+            currentPosition = currentPosition + 2;
+            iterator.setIndex(end);
+        } while (iterator.current() != CharacterIterator.DONE);
+    }
+
+    // Create the formatted string from returned AttributedCharacterIterator
+    private String getText(AttributedCharacterIterator iterator) {
+        StringBuffer buffer = new StringBuffer();
+        for (char c = iterator.first(); c != CharacterIterator.DONE;
+                c = iterator.next()) {
+            buffer.append(c);
+        }
+        return buffer.toString();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/TestMutatingInstance.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8177552
+ * @summary Checks the functioning of compact number format by changing the
+ *          formatting parameters. For example, min fraction digits, grouping
+ *          size etc.
+ * @modules jdk.localedata
+ * @run testng/othervm TestMutatingInstance
+ */
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.CompactNumberFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class TestMutatingInstance {
+
+    private static final NumberFormat FORMAT_FRACTION = NumberFormat
+            .getCompactNumberInstance(new Locale("en"), NumberFormat.Style.LONG);
+
+    private static final CompactNumberFormat FORMAT_GROUPING = (CompactNumberFormat) NumberFormat
+            .getCompactNumberInstance(new Locale("en"), NumberFormat.Style.LONG);
+
+    private static final NumberFormat FORMAT_MININTEGER = NumberFormat
+            .getCompactNumberInstance(new Locale("en"), NumberFormat.Style.LONG);
+
+    private static final NumberFormat FORMAT_PARSEINTONLY = NumberFormat
+            .getCompactNumberInstance(new Locale("en"), NumberFormat.Style.LONG);
+
+    // No compact patterns are specified for this instance except at index 4.
+    // This is to test how the behaviour differs between compact number formatting
+    // and general number formatting
+    private static final NumberFormat FORMAT_NO_PATTERNS = new CompactNumberFormat(
+            "#,##0.0#", DecimalFormatSymbols.getInstance(Locale.US),
+            new String[]{"", "", "", "", "00K", "", "", "", "", "", "", "", "", "", ""});
+
+    @BeforeTest
+    public void mutateInstances() {
+        FORMAT_FRACTION.setMinimumFractionDigits(2);
+        FORMAT_GROUPING.setGroupingSize(3);
+        FORMAT_GROUPING.setGroupingUsed(true);
+        FORMAT_MININTEGER.setMinimumIntegerDigits(5);
+        FORMAT_PARSEINTONLY.setParseIntegerOnly(true);
+        FORMAT_PARSEINTONLY.setGroupingUsed(true);
+        // Setting min fraction digits and other fields does not effect
+        // the general number formatting behaviour, when no compact number
+        // patterns are specified
+        FORMAT_NO_PATTERNS.setMinimumFractionDigits(2);
+    }
+
+    @DataProvider(name = "format")
+    Object[][] compactFormatData() {
+        return new Object[][]{
+            {FORMAT_FRACTION, 1900, "1.90 thousand"},
+            {FORMAT_FRACTION, 1000, "1.00 thousand"},
+            {FORMAT_FRACTION, 9090.99, "9.09 thousand"},
+            {FORMAT_FRACTION, new BigDecimal(12346567890987654.32),
+                "12346.57 trillion"},
+            {FORMAT_FRACTION, new BigInteger("12346567890987654"),
+                "12346.57 trillion"},
+            {FORMAT_GROUPING, new BigDecimal(12346567890987654.32),
+                "12,347 trillion"},
+            {FORMAT_GROUPING, 100000, "100 thousand"},
+            {FORMAT_MININTEGER, 10000, "00010 thousand"},
+            {FORMAT_NO_PATTERNS, 100000, "100,000"},
+            {FORMAT_NO_PATTERNS, 1000.998, "1,001"},
+            {FORMAT_NO_PATTERNS, 10900, "10.90K"},
+            {FORMAT_NO_PATTERNS, new BigDecimal(12346567890987654.32), "12,346,567,890,987,654"},};
+    }
+
+    @DataProvider(name = "parse")
+    Object[][] compactParseData() {
+        return new Object[][]{
+            {FORMAT_FRACTION, "190 thousand", 190000L},
+            {FORMAT_FRACTION, "19.9 thousand", 19900L},
+            {FORMAT_GROUPING, "12,346 thousand", 12346000L},
+            {FORMAT_PARSEINTONLY, "12345 thousand", 12345000L},
+            {FORMAT_PARSEINTONLY, "12,345 thousand", 12345000L},
+            {FORMAT_PARSEINTONLY, "12.345 thousand", 12000L},};
+    }
+
+    @Test(dataProvider = "format")
+    public void formatCompactNumber(NumberFormat nf,
+            Object number, String expected) {
+        CompactFormatAndParseHelper.testFormat(nf, number, expected);
+    }
+
+    @Test(dataProvider = "parse")
+    public void parseCompactNumber(NumberFormat nf,
+            String parseString, Number expected) throws ParseException {
+        CompactFormatAndParseHelper.testParse(nf, parseString, expected, null, null);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/TestParseBigDecimal.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8177552
+ * @summary Checks CNF.parse() when parseBigDecimal is set to true
+ * @modules jdk.localedata
+ * @run testng/othervm TestParseBigDecimal
+ */
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.math.BigDecimal;
+import java.text.CompactNumberFormat;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+public class TestParseBigDecimal {
+
+    private static final CompactNumberFormat FORMAT_DZ_LONG = (CompactNumberFormat) NumberFormat
+            .getCompactNumberInstance(new Locale("dz"), NumberFormat.Style.LONG);
+
+    private static final CompactNumberFormat FORMAT_EN_US_SHORT = (CompactNumberFormat) NumberFormat
+            .getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
+
+    private static final CompactNumberFormat FORMAT_EN_LONG = (CompactNumberFormat) NumberFormat
+            .getCompactNumberInstance(new Locale("en"), NumberFormat.Style.LONG);
+
+    private static final CompactNumberFormat FORMAT_HI_IN_LONG = (CompactNumberFormat) NumberFormat
+            .getCompactNumberInstance(new Locale("hi", "IN"), NumberFormat.Style.LONG);
+
+    private static final CompactNumberFormat FORMAT_JA_JP_SHORT = (CompactNumberFormat) NumberFormat
+            .getCompactNumberInstance(Locale.JAPAN, NumberFormat.Style.SHORT);
+
+    private static final CompactNumberFormat FORMAT_IT_SHORT = (CompactNumberFormat) NumberFormat
+            .getCompactNumberInstance(new Locale("it"), NumberFormat.Style.SHORT);
+
+    private static final CompactNumberFormat FORMAT_SW_LONG = (CompactNumberFormat) NumberFormat
+            .getCompactNumberInstance(new Locale("sw"), NumberFormat.Style.LONG);
+
+    private static final CompactNumberFormat FORMAT_SE_SHORT = (CompactNumberFormat) NumberFormat
+            .getCompactNumberInstance(new Locale("se"), NumberFormat.Style.SHORT);
+
+    @BeforeTest
+    public void mutateInstances() {
+        FORMAT_DZ_LONG.setParseBigDecimal(true);
+        FORMAT_EN_US_SHORT.setParseBigDecimal(true);
+        FORMAT_EN_LONG.setParseBigDecimal(true);
+        FORMAT_HI_IN_LONG.setParseBigDecimal(true);
+        FORMAT_JA_JP_SHORT.setParseBigDecimal(true);
+        FORMAT_IT_SHORT.setParseBigDecimal(true);
+        FORMAT_SW_LONG.setParseBigDecimal(true);
+        FORMAT_SE_SHORT.setParseBigDecimal(true);
+    }
+
+    @DataProvider(name = "parse")
+    Object[][] compactParseData() {
+        return new Object[][]{
+            // compact number format instance, string to parse, parsed number
+            {FORMAT_DZ_LONG, "\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2"
+                + "\u0F42 \u0F21", new BigDecimal("1000")},
+            {FORMAT_DZ_LONG, "-\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2"
+                + "\u0F42 \u0F23", new BigDecimal("-3000")},
+            {FORMAT_DZ_LONG, "\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62"
+                + "\u0F0B\u0F66\u0F0B\u0F61\u0F0B \u0F21"
+                + "\u0F22\u0F23\u0F24\u0F25\u0F27", new BigDecimal("12345700000000000000")},
+            {FORMAT_DZ_LONG, "-\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62"
+                + "\u0F0B\u0F66\u0F0B\u0F61\u0F0B \u0F21"
+                + "\u0F22\u0F23\u0F24\u0F25\u0F27", new BigDecimal("-12345700000000000000")},
+            {FORMAT_EN_US_SHORT, "-0.0", new BigDecimal("-0.0")},
+            {FORMAT_EN_US_SHORT, "0", new BigDecimal("0")},
+            {FORMAT_EN_US_SHORT, "499", new BigDecimal("499")},
+            {FORMAT_EN_US_SHORT, "-499", new BigDecimal("-499")},
+            {FORMAT_EN_US_SHORT, "499.89", new BigDecimal("499.89")},
+            {FORMAT_EN_US_SHORT, "-499.89", new BigDecimal("-499.89")},
+            {FORMAT_EN_US_SHORT, "1K", new BigDecimal("1000")},
+            {FORMAT_EN_US_SHORT, "-1K", new BigDecimal("-1000")},
+            {FORMAT_EN_US_SHORT, "3K", new BigDecimal("3000")},
+            {FORMAT_EN_US_SHORT, "-3K", new BigDecimal("-3000")},
+            {FORMAT_EN_US_SHORT, "17K", new BigDecimal("17000")},
+            {FORMAT_EN_US_SHORT, "-17K", new BigDecimal("-17000")},
+            {FORMAT_EN_US_SHORT, "12345678901234567890",
+                new BigDecimal("12345678901234567890")},
+            {FORMAT_EN_US_SHORT, "12345679T", new BigDecimal("12345679000000000000")},
+            {FORMAT_EN_US_SHORT, "-12345679T", new BigDecimal("-12345679000000000000")},
+            {FORMAT_EN_US_SHORT, "599.01K", new BigDecimal("599010.00")},
+            {FORMAT_EN_US_SHORT, "-599.01K", new BigDecimal("-599010.00")},
+            {FORMAT_EN_US_SHORT, "599444444.90T", new BigDecimal("599444444900000000000.00")},
+            {FORMAT_EN_US_SHORT, "-599444444.90T", new BigDecimal("-599444444900000000000.00")},
+            {FORMAT_EN_US_SHORT, "123456789012345.5678K",
+                new BigDecimal("123456789012345567.8000")},
+            {FORMAT_EN_US_SHORT, "17.000K", new BigDecimal("17000.000")},
+            {FORMAT_EN_US_SHORT, "123.56678K", new BigDecimal("123566.78000")},
+            {FORMAT_EN_US_SHORT, "-123.56678K", new BigDecimal("-123566.78000")},
+            {FORMAT_EN_LONG, "999", new BigDecimal("999")},
+            {FORMAT_EN_LONG, "1 thousand", new BigDecimal("1000")},
+            {FORMAT_EN_LONG, "3 thousand", new BigDecimal("3000")},
+            {FORMAT_EN_LONG, "12345679 trillion", new BigDecimal("12345679000000000000")},
+            {FORMAT_HI_IN_LONG, "999", new BigDecimal("999")},
+            {FORMAT_HI_IN_LONG, "-999", new BigDecimal("-999")},
+            {FORMAT_HI_IN_LONG, "1 \u0939\u091C\u093C\u093E\u0930", new BigDecimal("1000")},
+            {FORMAT_HI_IN_LONG, "-1 \u0939\u091C\u093C\u093E\u0930", new BigDecimal("-1000")},
+            {FORMAT_HI_IN_LONG, "3 \u0939\u091C\u093C\u093E\u0930", new BigDecimal("3000")},
+            {FORMAT_HI_IN_LONG, "12345679 \u0916\u0930\u092C", new BigDecimal("1234567900000000000")},
+            {FORMAT_HI_IN_LONG, "-12345679 \u0916\u0930\u092C", new BigDecimal("-1234567900000000000")},
+            {FORMAT_JA_JP_SHORT, "-99", new BigDecimal("-99")},
+            {FORMAT_JA_JP_SHORT, "1\u4E07", new BigDecimal("10000")},
+            {FORMAT_JA_JP_SHORT, "30\u4E07", new BigDecimal("300000")},
+            {FORMAT_JA_JP_SHORT, "-30\u4E07", new BigDecimal("-300000")},
+            {FORMAT_JA_JP_SHORT, "12345679\u5146", new BigDecimal("12345679000000000000")},
+            {FORMAT_JA_JP_SHORT, "-12345679\u5146", new BigDecimal("-12345679000000000000")},
+            {FORMAT_IT_SHORT, "-99", new BigDecimal("-99")},
+            {FORMAT_IT_SHORT, "1\u00a0Mln", new BigDecimal("1000000")},
+            {FORMAT_IT_SHORT, "30\u00a0Mln", new BigDecimal("30000000")},
+            {FORMAT_IT_SHORT, "-30\u00a0Mln", new BigDecimal("-30000000")},
+            {FORMAT_IT_SHORT, "12345679\u00a0Bln", new BigDecimal("12345679000000000000")},
+            {FORMAT_IT_SHORT, "-12345679\u00a0Bln", new BigDecimal("-12345679000000000000")},
+            {FORMAT_SW_LONG, "-0.0", new BigDecimal("-0.0")},
+            {FORMAT_SW_LONG, "499", new BigDecimal("499")},
+            {FORMAT_SW_LONG, "elfu 1", new BigDecimal("1000")},
+            {FORMAT_SW_LONG, "elfu 3", new BigDecimal("3000")},
+            {FORMAT_SW_LONG, "elfu 17", new BigDecimal("17000")},
+            {FORMAT_SW_LONG, "elfu -3", new BigDecimal("-3000")},
+            {FORMAT_SW_LONG, "-499", new BigDecimal("-499")},
+            {FORMAT_SW_LONG, "elfu 1", new BigDecimal("1000")},
+            {FORMAT_SW_LONG, "elfu 3", new BigDecimal("3000")},
+            {FORMAT_SW_LONG, "elfu -3", new BigDecimal("-3000")},
+            {FORMAT_SW_LONG, "elfu 17", new BigDecimal("17000")},
+            {FORMAT_SW_LONG, "trilioni 12345679", new BigDecimal("12345679000000000000")},
+            {FORMAT_SW_LONG, "trilioni -12345679", new BigDecimal("-12345679000000000000")},
+            {FORMAT_SW_LONG, "elfu 599.01", new BigDecimal("599010.00")},
+            {FORMAT_SW_LONG, "elfu -599.01", new BigDecimal("-599010.00")},
+            {FORMAT_SE_SHORT, "999", new BigDecimal("999")},
+            {FORMAT_SE_SHORT, "8\u00a0mn", new BigDecimal("8000000")},
+            {FORMAT_SE_SHORT, "8\u00a0dt", new BigDecimal("8000")},
+            {FORMAT_SE_SHORT, "12345679\u00a0bn", new BigDecimal("12345679000000000000")},
+            {FORMAT_SE_SHORT, "12345679,89\u00a0bn", new BigDecimal("12345679890000000000.00")},
+            {FORMAT_SE_SHORT, "\u2212999", new BigDecimal("-999")},
+            {FORMAT_SE_SHORT, "\u22128\u00a0mn", new BigDecimal("-8000000")},
+            {FORMAT_SE_SHORT, "\u22128\u00a0dt", new BigDecimal("-8000")},
+            {FORMAT_SE_SHORT, "\u221212345679\u00a0bn", new BigDecimal("-12345679000000000000")},
+            {FORMAT_SE_SHORT, "\u221212345679,89\u00a0bn", new BigDecimal("-12345679890000000000.00")},};
+    }
+
+    @Test(dataProvider = "parse")
+    public void testParse(NumberFormat cnf, String parseString,
+            Number expected) throws ParseException {
+        CompactFormatAndParseHelper.testParse(cnf, parseString, expected, null, BigDecimal.class);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/TestSpecialValues.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8177552
+ * @summary Checks the formatting and parsing of special values
+ * @modules jdk.localedata
+ * @run testng/othervm TestSpecialValues
+ */
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class TestSpecialValues {
+
+    private static final NumberFormat FORMAT = NumberFormat
+            .getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
+
+    @DataProvider(name = "formatSpecialValues")
+    Object[][] formatSpecialValues() {
+        return new Object[][]{
+            // number , formatted ouput
+            {+0.0, "0"},
+            {-0.0, "-0"},
+            {Double.MIN_VALUE, "0"},
+            {Double.MIN_NORMAL, "0"},
+            {Double.NaN, "NaN"},
+            {Double.POSITIVE_INFINITY, "\u221E"},
+            {Double.NEGATIVE_INFINITY, "-\u221E"},
+            {Long.MIN_VALUE, "-9223372T"},
+            {Long.MAX_VALUE, "9223372T"},};
+    }
+
+    @DataProvider(name = "parseSpecialValues")
+    Object[][] parseSpecialValues() {
+        return new Object[][]{
+            // parse string, parsed number
+            {"-0.0", -0.0},
+            {"" + Long.MIN_VALUE, Long.MIN_VALUE},
+            {"" + Long.MAX_VALUE, Long.MAX_VALUE},
+            {"NaN", Double.NaN},
+            {"\u221E", Double.POSITIVE_INFINITY},
+            {"-\u221E", Double.NEGATIVE_INFINITY},};
+    }
+
+    @Test(dataProvider = "formatSpecialValues")
+    public void testFormatSpecialValues(Object number, String expected) {
+        CompactFormatAndParseHelper.testFormat(FORMAT, number, expected);
+    }
+
+    @Test(dataProvider = "parseSpecialValues")
+    public void testParseSpecialValues(String parseString, Number expected)
+            throws ParseException {
+        CompactFormatAndParseHelper.testParse(FORMAT, parseString, expected, null, null);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/TestUExtensionOverride.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8177552
+ * @summary Checks the behaviour of Unicode BCP 47 U Extension with
+ *          compact number format
+ * @modules jdk.localedata
+ * @run testng/othervm TestUExtensionOverride
+ */
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class TestUExtensionOverride {
+
+    @DataProvider(name = "compactFormatData")
+    Object[][] compactFormatData() {
+        return new Object[][]{
+            // locale, number, formatted string
+
+            // -nu
+            {Locale.forLanguageTag("en-US-u-nu-deva"), 12345, "\u0967\u0968K"},
+            {Locale.forLanguageTag("en-US-u-nu-sinh"), 12345, "\u0de7\u0de8K"},
+            {Locale.forLanguageTag("en-US-u-nu-zzzz"), 12345, "12K"},
+            // -rg
+            {Locale.forLanguageTag("fr-FR-u-rg-cazzzz"), 1234567,
+                "1\u00a0234\u00a0567"},
+            {Locale.forLanguageTag("fr-FR-u-rg-cazzzz"), 1234567890,
+                "1\u00a0G"},
+            // -nu and -rg
+            {Locale.forLanguageTag("en-US-u-nu-deva-rg-dezzzz"), 12345,
+                "\u0967\u0968K"},
+            {Locale.forLanguageTag("fr-FR-u-nu-zzzz-rg-cazzzz"), 1234567890,
+                "1\u00a0Md"},
+            {Locale.forLanguageTag("fr-FR-u-nu-zzzz-rg-zzzz"), 12345,
+                "12\u00a0k"},
+            {Locale.forLanguageTag("fr-FR-u-rg-cazzzz-nu-deva"), 12345,
+                "\u0967\u0968\u00a0k"},};
+    }
+
+    @DataProvider(name = "compactParseData")
+    Object[][] compactParseData() {
+        return new Object[][]{
+            // locale, parse string, parsed number
+
+            // -nu
+            {Locale.forLanguageTag("en-US-u-nu-deva"),
+                "\u0967\u0968K", 12000L},
+            {Locale.forLanguageTag("en-US-u-nu-sinh"),
+                "\u0de7\u0de8K", 12000L},
+            {Locale.forLanguageTag("en-US-u-nu-zzzz"),
+                "12K", 12000L},
+            // -rg
+            {Locale.forLanguageTag("fr-FR-u-rg-cazzzz"),
+                "1\u00a0G", 1000000000L},
+            // -nu and -rg
+            {Locale.forLanguageTag("en-US-u-nu-deva-rg-dezzzz"),
+                "\u0967\u0968K", 12000L},
+            {Locale.forLanguageTag("fr-FR-u-nu-zzzz-rg-cazzzz"),
+                "1\u00a0Md", 1000000000L},
+            {Locale.forLanguageTag("fr-FR-u-nu-zzzz-rg-zzzz"),
+                "12\u00a0k", 12000L},
+            {Locale.forLanguageTag("fr-FR-u-rg-cazzzz-nu-deva"),
+                "\u0967\u0968\u00a0k", 12000L},};
+    }
+
+    @Test(dataProvider = "compactFormatData")
+    public void testFormat(Locale locale, double num,
+            String expected) {
+        NumberFormat cnf = NumberFormat.getCompactNumberInstance(locale,
+                NumberFormat.Style.SHORT);
+        CompactFormatAndParseHelper.testFormat(cnf, num, expected);
+    }
+
+    @Test(dataProvider = "compactParseData")
+    public void testParse(Locale locale, String parseString,
+            Number expected) throws ParseException {
+        NumberFormat cnf = NumberFormat.getCompactNumberInstance(locale,
+                NumberFormat.Style.SHORT);
+        CompactFormatAndParseHelper.testParse(cnf, parseString, expected, null, null);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/TestWithCompatProvider.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8177552
+ * @summary Checks the compact number format with COMPAT provider. Since the
+ *          compact number resources are only provided by CLDR, using COMPAT
+ *          as a provider should always use the default patterns added in the
+ *          FormatData.java resource bundle
+ * @modules jdk.localedata
+ * @run testng/othervm -Djava.locale.providers=COMPAT TestWithCompatProvider
+ */
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class TestWithCompatProvider {
+
+    private static final NumberFormat FORMAT_DZ_SHORT = NumberFormat
+            .getCompactNumberInstance(new Locale("dz"), NumberFormat.Style.SHORT);
+
+    private static final NumberFormat FORMAT_EN_US_SHORT = NumberFormat
+            .getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
+
+    @DataProvider(name = "format")
+    Object[][] compactFormatData() {
+        return new Object[][]{
+            {FORMAT_DZ_SHORT, 1000.09, "1K"},
+            {FORMAT_DZ_SHORT, -999.99, "-1K"},
+            {FORMAT_DZ_SHORT, -0.0, "-0"},
+            {FORMAT_DZ_SHORT, new BigInteger("12345678901234567890"), "12345679T"},
+            {FORMAT_DZ_SHORT, new BigDecimal("12345678901234567890.89"), "12345679T"},
+            {FORMAT_EN_US_SHORT, -999.99, "-1K"},
+            {FORMAT_EN_US_SHORT, 9999, "10K"},
+            {FORMAT_EN_US_SHORT, 3000.90, "3K"},
+            {FORMAT_EN_US_SHORT, new BigInteger("12345678901234567890"), "12345679T"},
+            {FORMAT_EN_US_SHORT, new BigDecimal("12345678901234567890.89"), "12345679T"},};
+    }
+
+    @DataProvider(name = "parse")
+    Object[][] compactParseData() {
+        return new Object[][]{
+            {FORMAT_DZ_SHORT, "1K", 1000L},
+            {FORMAT_DZ_SHORT, "-3K", -3000L},
+            {FORMAT_DZ_SHORT, "12345700T", 1.23457E19},
+            {FORMAT_EN_US_SHORT, "-99", -99L},
+            {FORMAT_EN_US_SHORT, "10K", 10000L},
+            {FORMAT_EN_US_SHORT, "12345679T", 1.2345679E19},};
+    }
+
+    @Test(dataProvider = "format")
+    public void testFormat(NumberFormat cnf, Object number,
+            String expected) {
+        CompactFormatAndParseHelper.testFormat(cnf, number, expected);
+    }
+
+    @Test(dataProvider = "parse")
+    public void testParse(NumberFormat cnf, String parseString,
+            Number expected) throws ParseException {
+        CompactFormatAndParseHelper.testParse(cnf, parseString, expected, null, null);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/serialization/TestDeserializeCNF.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8177552
+ * @modules jdk.localedata
+ * @summary Checks deserialization of compact number format
+ * @library /java/text/testlib
+ * @build TestDeserializeCNF HexDumpReader
+ * @run testng/othervm TestDeserializeCNF
+ */
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.math.RoundingMode;
+import java.text.CompactNumberFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
+import static org.testng.Assert.*;
+
+public class TestDeserializeCNF {
+
+    // This object is serialized in cnf1.ser.txt with HALF_UP
+    // rounding mode, groupingsize = 3 and parseBigDecimal = true
+    private static final CompactNumberFormat COMPACT_FORMAT1 = new CompactNumberFormat("#,##0.###",
+            DecimalFormatSymbols.getInstance(Locale.US),
+            new String[]{"", "", "", "0K", "00K", "000K", "0M", "00M", "000M", "0B", "00B", "000B", "0T", "00T", "000T"});
+
+    // This object is serialized in cnf2.ser.txt with min integer digits = 20
+    // and min fraction digits = 5
+    private static final CompactNumberFormat COMPACT_FORMAT2 = new CompactNumberFormat("#,##0.###",
+            DecimalFormatSymbols.getInstance(Locale.JAPAN),
+            new String[]{"", "", "", "0", "0\u4e07", "00\u4e07", "000\u4e07", "0000\u4e07", "0\u5104", "00\u5104", "000\u5104", "0000\u5104", "0\u5146", "00\u5146", "000\u5146"});
+
+    private static final String FILE_COMPACT_FORMAT1 = "cnf1.ser.txt";
+    private static final String FILE_COMPACT_FORMAT2 = "cnf2.ser.txt";
+
+    @BeforeTest
+    public void mutateInstances() {
+        COMPACT_FORMAT1.setRoundingMode(RoundingMode.HALF_UP);
+        COMPACT_FORMAT1.setGroupingSize(3);
+        COMPACT_FORMAT1.setParseBigDecimal(true);
+
+        COMPACT_FORMAT2.setMinimumIntegerDigits(20);
+        COMPACT_FORMAT2.setMinimumFractionDigits(5);
+    }
+
+    @Test
+    public void testDeserialization() throws IOException, ClassNotFoundException {
+        try (InputStream istream1 = HexDumpReader.getStreamFromHexDump(FILE_COMPACT_FORMAT1);
+                ObjectInputStream ois1 = new ObjectInputStream(istream1);
+                InputStream istream2 = HexDumpReader.getStreamFromHexDump(FILE_COMPACT_FORMAT2);
+                ObjectInputStream ois2 = new ObjectInputStream(istream2);) {
+
+            CompactNumberFormat obj1 = (CompactNumberFormat) ois1.readObject();
+            assertEquals(obj1, COMPACT_FORMAT1, "Deserialized instance is not"
+                    + " equal to the instance serialized in " + FILE_COMPACT_FORMAT1);
+
+            CompactNumberFormat obj2 = (CompactNumberFormat) ois2.readObject();
+            assertEquals(obj2, COMPACT_FORMAT2, "Deserialized instance is not"
+                    + " equal to the instance serialized in " + FILE_COMPACT_FORMAT2);
+        }
+    }
+
+    // The objects are serialized using the serialize() method, the hex
+    // dump printed is copied to respective object files
+//    void serialize(CompactNumberFormat cnf) {
+//        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+//        try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+//            oos.writeObject(cnf);
+//        } catch (IOException ioe) {
+//            throw new RuntimeException(ioe);
+//        }
+//        byte[] ser = baos.toByteArray();
+//        for (byte b : ser) {
+//            System.out.print("" + String.format("%02x", b));
+//        }
+//    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/serialization/TestSerialization.java	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8177552
+ * @modules jdk.localedata
+ * @summary Checks the serialization feature of CompactNumberFormat
+ * @run testng/othervm TestSerialization
+ */
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.RoundingMode;
+import java.text.CompactNumberFormat;
+import java.text.NumberFormat;
+import java.util.Locale;
+import static org.testng.Assert.*;
+
+public class TestSerialization {
+
+    private static final NumberFormat FORMAT_HI = NumberFormat.getCompactNumberInstance(
+            new Locale("hi"), NumberFormat.Style.SHORT);
+    private static final CompactNumberFormat FORMAT_EN_US = (CompactNumberFormat) NumberFormat
+            .getCompactNumberInstance(Locale.US, NumberFormat.Style.LONG);
+    private static final NumberFormat FORMAT_JA_JP = NumberFormat.getCompactNumberInstance(
+            Locale.JAPAN, NumberFormat.Style.SHORT);
+    private static final NumberFormat FORMAT_FR_FR = NumberFormat.getCompactNumberInstance(
+            Locale.FRANCE, NumberFormat.Style.LONG);
+    private static final NumberFormat FORMAT_DE_DE = NumberFormat.getCompactNumberInstance(
+            Locale.GERMANY, NumberFormat.Style.SHORT);
+    private static final NumberFormat FORMAT_KO_KR = NumberFormat.getCompactNumberInstance(
+            Locale.KOREA, NumberFormat.Style.SHORT);
+
+    @BeforeTest
+    public void mutateInstances() {
+        FORMAT_HI.setMinimumFractionDigits(2);
+        FORMAT_HI.setMinimumIntegerDigits(5);
+
+        FORMAT_EN_US.setRoundingMode(RoundingMode.HALF_UP);
+        FORMAT_EN_US.setGroupingSize(3);
+        FORMAT_EN_US.setParseBigDecimal(true);
+
+        FORMAT_JA_JP.setMaximumFractionDigits(30);
+        FORMAT_JA_JP.setMaximumIntegerDigits(30);
+
+        FORMAT_FR_FR.setParseIntegerOnly(true);
+        FORMAT_FR_FR.setGroupingUsed(true);
+
+        // Setting minimum integer digits beyond the allowed range
+        FORMAT_DE_DE.setMinimumIntegerDigits(320);
+
+        // Setting minimum fraction digits beyond the allowed range
+        FORMAT_KO_KR.setMinimumFractionDigits(350);
+    }
+
+    @Test
+    public void testSerialization() throws IOException, ClassNotFoundException {
+        // Serialize
+        serialize("cdf.ser", FORMAT_HI, FORMAT_EN_US, FORMAT_JA_JP, FORMAT_FR_FR, FORMAT_DE_DE, FORMAT_KO_KR);
+        // Deserialize
+        deserialize("cdf.ser", FORMAT_HI, FORMAT_EN_US, FORMAT_JA_JP, FORMAT_FR_FR, FORMAT_DE_DE, FORMAT_KO_KR);
+    }
+
+    private void serialize(String fileName, NumberFormat... formats)
+            throws IOException {
+        try (ObjectOutputStream os = new ObjectOutputStream(
+                new FileOutputStream(fileName))) {
+            for (NumberFormat fmt : formats) {
+                os.writeObject(fmt);
+            }
+        }
+    }
+
+    private static void deserialize(String fileName, NumberFormat... formats)
+            throws IOException, ClassNotFoundException {
+        try (ObjectInputStream os = new ObjectInputStream(
+                new FileInputStream(fileName))) {
+            for (NumberFormat fmt : formats) {
+                NumberFormat obj = (NumberFormat) os.readObject();
+                assertEquals(fmt, obj, "Serialized and deserialized"
+                        + " objects do not match");
+
+                long number = 123456789789L;
+                String expected = fmt.format(number);
+                String actual = obj.format(number);
+                assertEquals(actual, expected, "Serialized and deserialized"
+                        + " objects are expected to return same formatted"
+                        + " output for number: " + number);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/serialization/cnf1.ser.txt	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,64 @@
+#
+# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+# Hex dump of a serialized CompactNumberFormat for TestDeserializeCNF.
+
+aced00057372001d6a6176612e746578742e436f6d706163744e756d626572466f726d
+617462ed0c37b8207cf602000642000c67726f7570696e6753697a655a000f70617273
+65426967446563696d616c5b000f636f6d706163745061747465726e737400135b4c6a
+6176612f6c616e672f537472696e673b4c000e646563696d616c5061747465726e7400
+124c6a6176612f6c616e672f537472696e673b4c000c726f756e64696e674d6f646574
+00184c6a6176612f6d6174682f526f756e64696e674d6f64653b4c000773796d626f6c
+737400204c6a6176612f746578742f446563696d616c466f726d617453796d626f6c73
+3b787200166a6176612e746578742e4e756d626572466f726d6174dff6b3bf137d07e8
+03000b5a000c67726f7570696e67557365644200116d61784672616374696f6e446967
+6974734200106d6178496e74656765724469676974734900156d6178696d756d467261
+6374696f6e4469676974734900146d6178696d756d496e746567657244696769747342
+00116d696e4672616374696f6e4469676974734200106d696e496e7465676572446967
+6974734900156d696e696d756d4672616374696f6e4469676974734900146d696e696d
+756d496e74656765724469676974735a00107061727365496e74656765724f6e6c7949
+001573657269616c56657273696f6e4f6e53747265616d787200106a6176612e746578
+742e466f726d6174fbd8bc12e90f1843020000787000007f0000000000000135000100
+000000000000010000000001780301757200135b4c6a6176612e6c616e672e53747269
+6e673badd256e7e91d7b4702000078700000000f74000071007e000a71007e000a7400
+02304b74000330304b7400043030304b740002304d74000330304d7400043030304d74
+0002304274000330304274000430303042740002305474000330305474000430303054
+740009232c2323302e2323237e7200166a6176612e6d6174682e526f756e64696e674d
+6f646500000000000000001200007872000e6a6176612e6c616e672e456e756d000000
+0000000000120000787074000748414c465f55507372001e6a6176612e746578742e44
+6563696d616c466f726d617453796d626f6c73501d17990868939c0200114300106465
+63696d616c536570617261746f72430005646967697443000b6578706f6e656e746961
+6c43001167726f7570696e67536570617261746f724300096d696e75735369676e4300
+116d6f6e6574617279536570617261746f724300107061747465726e53657061726174
+6f724300077065724d696c6c43000770657263656e7449001573657269616c56657273
+696f6e4f6e53747265616d4300097a65726f44696769744c00034e614e71007e00024c
+000e63757272656e637953796d626f6c71007e00024c00146578706f6e656e7469616c
+536570617261746f7271007e00024c0008696e66696e69747971007e00024c0012696e
+746c43757272656e637953796d626f6c71007e00024c00066c6f63616c657400124c6a
+6176612f7574696c2f4c6f63616c653b7870002e00230045002c002d002e003b203000
+250000000300307400034e614e7074000145740003e2889e70737200106a6176612e75
+74696c2e4c6f63616c657ef811609c30f9ec03000649000868617368636f64654c0007
+636f756e74727971007e00024c000a657874656e73696f6e7371007e00024c00086c61
+6e677561676571007e00024c000673637269707471007e00024c000776617269616e74
+71007e00027870ffffffff740002555371007e000a740002656e71007e000a71007e00
+0a78
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/CompactNumberFormat/serialization/cnf2.ser.txt	Thu Dec 06 12:39:28 2018 +0530
@@ -0,0 +1,64 @@
+#
+# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+# Hex dump of a serialized CompactNumberFormat for TestDeserializeCNF.
+
+aced00057372001d6a6176612e746578742e436f6d706163744e756d626572466f726d
+617462ed0c37b8207cf602000642000c67726f7570696e6753697a655a000f70617273
+65426967446563696d616c5b000f636f6d706163745061747465726e737400135b4c6a
+6176612f6c616e672f537472696e673b4c000e646563696d616c5061747465726e7400
+124c6a6176612f6c616e672f537472696e673b4c000c726f756e64696e674d6f646574
+00184c6a6176612f6d6174682f526f756e64696e674d6f64653b4c000773796d626f6c
+737400204c6a6176612f746578742f446563696d616c466f726d617453796d626f6c73
+3b787200166a6176612e746578742e4e756d626572466f726d6174dff6b3bf137d07e8
+03000b5a000c67726f7570696e67557365644200116d61784672616374696f6e446967
+6974734200106d6178496e74656765724469676974734900156d6178696d756d467261
+6374696f6e4469676974734900146d6178696d756d496e746567657244696769747342
+00116d696e4672616374696f6e4469676974734200106d696e496e7465676572446967
+6974734900156d696e696d756d4672616374696f6e4469676974734900146d696e696d
+756d496e74656765724469676974735a00107061727365496e74656765724f6e6c7949
+001573657269616c56657273696f6e4f6e53747265616d787200106a6176612e746578
+742e466f726d6174fbd8bc12e90f1843020000787000057f0000000500000135051400
+000005000000140000000001780000757200135b4c6a6176612e6c616e672e53747269
+6e673badd256e7e91d7b4702000078700000000f74000071007e000a71007e000a7400
+013074000430e4b8877400053030e4b887740006303030e4b88774000730303030e4b8
+8774000430e584847400053030e58484740006303030e5848474000730303030e58484
+74000430e585867400053030e58586740006303030e58586740009232c2323302e2323
+237e7200166a6176612e6d6174682e526f756e64696e674d6f64650000000000000000
+1200007872000e6a6176612e6c616e672e456e756d0000000000000000120000787074
+000948414c465f4556454e7372001e6a6176612e746578742e446563696d616c466f72
+6d617453796d626f6c73501d17990868939c020011430010646563696d616c53657061
+7261746f72430005646967697443000b6578706f6e656e7469616c43001167726f7570
+696e67536570617261746f724300096d696e75735369676e4300116d6f6e6574617279
+536570617261746f724300107061747465726e536570617261746f724300077065724d
+696c6c43000770657263656e7449001573657269616c56657273696f6e4f6e53747265
+616d4300097a65726f44696769744c00034e614e71007e00024c000e63757272656e63
+7953796d626f6c71007e00024c00146578706f6e656e7469616c536570617261746f72
+71007e00024c0008696e66696e69747971007e00024c0012696e746c43757272656e63
+7953796d626f6c71007e00024c00066c6f63616c657400124c6a6176612f7574696c2f
+4c6f63616c653b7870002e00230045002c002d002e003b203000250000000300307400
+034e614e7074000145740003e2889e70737200106a6176612e7574696c2e4c6f63616c
+657ef811609c30f9ec03000649000868617368636f64654c0007636f756e7472797100
+7e00024c000a657874656e73696f6e7371007e00024c00086c616e677561676571007e
+00024c000673637269707471007e00024c000776617269616e7471007e00027870ffff
+ffff7400024a5071007e000a7400026a6171007e000a71007e000a78