]> granicus.if.org Git - icu/commitdiff
ICU-13513 Merging trunk to branch (includes the big reformatting commit).
authorShane Carr <shane@unicode.org>
Sat, 23 Dec 2017 03:13:03 +0000 (03:13 +0000)
committerShane Carr <shane@unicode.org>
Sat, 23 Dec 2017 03:13:03 +0000 (03:13 +0000)
X-SVN-Rev: 40751

1  2 
icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixPatternProvider.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixUtils.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternStringParser.java
icu4j/main/classes/core/src/com/ibm/icu/number/NumberPropertyMapper.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixUtilsTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java

index daf22c2905198d091f2abf7b16d46546c4f5e63c,02cda7dd551e09f89db211f1481327858fa15ab5..01d137fceef383d638c9d8466f2208357c9c6ba4
@@@ -3,32 -3,24 +3,32 @@@
  package com.ibm.icu.impl.number;
  
  public interface AffixPatternProvider {
-   public static final class Flags {
-     public static final int PLURAL_MASK = 0xff;
-     public static final int PREFIX = 0x100;
-     public static final int NEGATIVE_SUBPATTERN = 0x200;
-     public static final int PADDING = 0x400;
-   }
+     public static final class Flags {
+         public static final int PLURAL_MASK = 0xff;
+         public static final int PREFIX = 0x100;
+         public static final int NEGATIVE_SUBPATTERN = 0x200;
+         public static final int PADDING = 0x400;
+     }
  
-   // Convenience compound flags
-   public static final int FLAG_POS_PREFIX = Flags.PREFIX;
-   public static final int FLAG_POS_SUFFIX = 0;
-   public static final int FLAG_NEG_PREFIX = Flags.PREFIX | Flags.NEGATIVE_SUBPATTERN;
-   public static final int FLAG_NEG_SUFFIX = Flags.NEGATIVE_SUBPATTERN;
++    // Convenience compound flags
++    public static final int FLAG_POS_PREFIX = Flags.PREFIX;
++    public static final int FLAG_POS_SUFFIX = 0;
++    public static final int FLAG_NEG_PREFIX = Flags.PREFIX | Flags.NEGATIVE_SUBPATTERN;
++    public static final int FLAG_NEG_SUFFIX = Flags.NEGATIVE_SUBPATTERN;
 +
-   public char charAt(int flags, int i);
+     public char charAt(int flags, int i);
  
-   public int length(int flags);
+     public int length(int flags);
  
-   public String getString(int flags);
++    public String getString(int flags);
 +
-   public boolean hasCurrencySign();
+     public boolean hasCurrencySign();
  
-   public boolean positiveHasPlusSign();
+     public boolean positiveHasPlusSign();
  
-   public boolean hasNegativeSubpattern();
+     public boolean hasNegativeSubpattern();
  
-   public boolean negativeHasMinusSign();
+     public boolean negativeHasMinusSign();
  
-   public boolean containsSymbolType(int type);
+     public boolean containsSymbolType(int type);
  }
index d5706e3d0175ef96e9f9b57e4f4245c939d5ec7c,2cf6d06b74e346eda566169125a1966f5a28c08d..73d717d4f5d3e0ac719b536cd520dd5162ae037f
@@@ -46,609 -60,600 +61,637 @@@ import com.ibm.icu.text.UnicodeSet
   */
  public class AffixUtils {
  
-   private static final int STATE_BASE = 0;
-   private static final int STATE_FIRST_QUOTE = 1;
-   private static final int STATE_INSIDE_QUOTE = 2;
-   private static final int STATE_AFTER_QUOTE = 3;
-   private static final int STATE_FIRST_CURR = 4;
-   private static final int STATE_SECOND_CURR = 5;
-   private static final int STATE_THIRD_CURR = 6;
-   private static final int STATE_FOURTH_CURR = 7;
-   private static final int STATE_FIFTH_CURR = 8;
-   private static final int STATE_OVERFLOW_CURR = 9;
+     private static final int STATE_BASE = 0;
+     private static final int STATE_FIRST_QUOTE = 1;
+     private static final int STATE_INSIDE_QUOTE = 2;
+     private static final int STATE_AFTER_QUOTE = 3;
+     private static final int STATE_FIRST_CURR = 4;
+     private static final int STATE_SECOND_CURR = 5;
+     private static final int STATE_THIRD_CURR = 6;
+     private static final int STATE_FOURTH_CURR = 7;
+     private static final int STATE_FIFTH_CURR = 8;
+     private static final int STATE_OVERFLOW_CURR = 9;
  
-   /** Represents a literal character; the value is stored in the code point field. */
-   private static final int TYPE_CODEPOINT = 0;
+     /** Represents a literal character; the value is stored in the code point field. */
+     private static final int TYPE_CODEPOINT = 0;
  
-   /** Represents a minus sign symbol '-'. */
-   public static final int TYPE_MINUS_SIGN = -1;
+     /** Represents a minus sign symbol '-'. */
+     public static final int TYPE_MINUS_SIGN = -1;
  
-   /** Represents a plus sign symbol '+'. */
-   public static final int TYPE_PLUS_SIGN = -2;
+     /** Represents a plus sign symbol '+'. */
+     public static final int TYPE_PLUS_SIGN = -2;
  
-   /** Represents a percent sign symbol '%'. */
-   public static final int TYPE_PERCENT = -3;
+     /** Represents a percent sign symbol '%'. */
+     public static final int TYPE_PERCENT = -3;
  
-   /** Represents a permille sign symbol '‰'. */
-   public static final int TYPE_PERMILLE = -4;
+     /** Represents a permille sign symbol '‰'. */
+     public static final int TYPE_PERMILLE = -4;
  
-   /** Represents a single currency symbol '¤'. */
-   public static final int TYPE_CURRENCY_SINGLE = -5;
+     /** Represents a single currency symbol '¤'. */
+     public static final int TYPE_CURRENCY_SINGLE = -5;
  
-   /** Represents a double currency symbol '¤¤'. */
-   public static final int TYPE_CURRENCY_DOUBLE = -6;
+     /** Represents a double currency symbol '¤¤'. */
+     public static final int TYPE_CURRENCY_DOUBLE = -6;
  
-   /** Represents a triple currency symbol '¤¤¤'. */
-   public static final int TYPE_CURRENCY_TRIPLE = -7;
+     /** Represents a triple currency symbol '¤¤¤'. */
+     public static final int TYPE_CURRENCY_TRIPLE = -7;
  
-   /** Represents a quadruple currency symbol '¤¤¤¤'. */
-   public static final int TYPE_CURRENCY_QUAD = -8;
+     /** Represents a quadruple currency symbol '¤¤¤¤'. */
+     public static final int TYPE_CURRENCY_QUAD = -8;
  
-   /** Represents a quintuple currency symbol '¤¤¤¤¤'. */
-   public static final int TYPE_CURRENCY_QUINT = -9;
+     /** Represents a quintuple currency symbol '¤¤¤¤¤'. */
+     public static final int TYPE_CURRENCY_QUINT = -9;
  
-   /** Represents a sequence of six or more currency symbols. */
-   public static final int TYPE_CURRENCY_OVERFLOW = -15;
+     /** Represents a sequence of six or more currency symbols. */
+     public static final int TYPE_CURRENCY_OVERFLOW = -15;
  
-   public static interface SymbolProvider {
-     public CharSequence getSymbol(int type);
-   }
+     public static interface SymbolProvider {
+         public CharSequence getSymbol(int type);
+     }
  
-   /**
-    * Estimates the number of code points present in an unescaped version of the affix pattern string
-    * (one that would be returned by {@link #unescape}), assuming that all interpolated symbols
-    * consume one code point and that currencies consume as many code points as their symbol width.
-    * Used for computing padding width.
-    *
-    * @param patternString The original string whose width will be estimated.
-    * @return The length of the unescaped string.
-    */
-   public static int estimateLength(CharSequence patternString) {
-     if (patternString == null) return 0;
-     int state = STATE_BASE;
-     int offset = 0;
-     int length = 0;
-     for (; offset < patternString.length(); ) {
-       int cp = Character.codePointAt(patternString, offset);
-       switch (state) {
-         case STATE_BASE:
-           if (cp == '\'') {
-             // First quote
-             state = STATE_FIRST_QUOTE;
-           } else {
-             // Unquoted symbol
-             length++;
-           }
-           break;
+     /**
+      * Estimates the number of code points present in an unescaped version of the affix pattern string
+      * (one that would be returned by {@link #unescape}), assuming that all interpolated symbols consume
+      * one code point and that currencies consume as many code points as their symbol width. Used for
+      * computing padding width.
+      *
+      * @param patternString
+      *            The original string whose width will be estimated.
+      * @return The length of the unescaped string.
+      */
+     public static int estimateLength(CharSequence patternString) {
+         if (patternString == null)
+             return 0;
+         int state = STATE_BASE;
+         int offset = 0;
+         int length = 0;
+         for (; offset < patternString.length();) {
+             int cp = Character.codePointAt(patternString, offset);
+             switch (state) {
+             case STATE_BASE:
+                 if (cp == '\'') {
+                     // First quote
+                     state = STATE_FIRST_QUOTE;
+                 } else {
+                     // Unquoted symbol
+                     length++;
+                 }
+                 break;
+             case STATE_FIRST_QUOTE:
+                 if (cp == '\'') {
+                     // Repeated quote
+                     length++;
+                     state = STATE_BASE;
+                 } else {
+                     // Quoted code point
+                     length++;
+                     state = STATE_INSIDE_QUOTE;
+                 }
+                 break;
+             case STATE_INSIDE_QUOTE:
+                 if (cp == '\'') {
+                     // End of quoted sequence
+                     state = STATE_AFTER_QUOTE;
+                 } else {
+                     // Quoted code point
+                     length++;
+                 }
+                 break;
+             case STATE_AFTER_QUOTE:
+                 if (cp == '\'') {
+                     // Double quote inside of quoted sequence
+                     length++;
+                     state = STATE_INSIDE_QUOTE;
+                 } else {
+                     // Unquoted symbol
+                     length++;
+                 }
+                 break;
+             default:
+                 throw new AssertionError();
+             }
+             offset += Character.charCount(cp);
+         }
+         switch (state) {
          case STATE_FIRST_QUOTE:
-           if (cp == '\'') {
-             // Repeated quote
-             length++;
-             state = STATE_BASE;
-           } else {
-             // Quoted code point
-             length++;
-             state = STATE_INSIDE_QUOTE;
-           }
-           break;
          case STATE_INSIDE_QUOTE:
-           if (cp == '\'') {
-             // End of quoted sequence
-             state = STATE_AFTER_QUOTE;
-           } else {
-             // Quoted code point
-             length++;
-           }
-           break;
-         case STATE_AFTER_QUOTE:
-           if (cp == '\'') {
-             // Double quote inside of quoted sequence
-             length++;
-             state = STATE_INSIDE_QUOTE;
-           } else {
-             // Unquoted symbol
-             length++;
-           }
-           break;
+             throw new IllegalArgumentException("Unterminated quote: \"" + patternString + "\"");
          default:
-           throw new AssertionError();
-       }
+             break;
+         }
  
-       offset += Character.charCount(cp);
+         return length;
      }
  
-     switch (state) {
-       case STATE_FIRST_QUOTE:
-       case STATE_INSIDE_QUOTE:
-         throw new IllegalArgumentException("Unterminated quote: \"" + patternString + "\"");
-       default:
-         break;
-     }
+     /**
+      * Takes a string and escapes (quotes) characters that have special meaning in the affix pattern
+      * syntax. This function does not reverse-lookup symbols.
+      *
+      * <p>
+      * Example input: "-$x"; example output: "'-'$x"
+      *
+      * @param input
+      *            The string to be escaped.
+      * @param output
+      *            The string builder to which to append the escaped string.
+      * @return The number of chars (UTF-16 code units) appended to the output.
+      */
+     public static int escape(CharSequence input, StringBuilder output) {
+         if (input == null)
+             return 0;
+         int state = STATE_BASE;
+         int offset = 0;
+         int startLength = output.length();
+         for (; offset < input.length();) {
+             int cp = Character.codePointAt(input, offset);
+             switch (cp) {
+             case '\'':
+                 output.append("''");
+                 break;
  
-     return length;
-   }
+             case '-':
+             case '+':
+             case '%':
+             case '‰':
+             case '¤':
+                 if (state == STATE_BASE) {
+                     output.append('\'');
+                     output.appendCodePoint(cp);
+                     state = STATE_INSIDE_QUOTE;
+                 } else {
+                     output.appendCodePoint(cp);
+                 }
+                 break;
  
-   /**
-    * Takes a string and escapes (quotes) characters that have special meaning in the affix pattern
-    * syntax. This function does not reverse-lookup symbols.
-    *
-    * <p>Example input: "-$x"; example output: "'-'$x"
-    *
-    * @param input The string to be escaped.
-    * @param output The string builder to which to append the escaped string.
-    * @return The number of chars (UTF-16 code units) appended to the output.
-    */
-   public static int escape(CharSequence input, StringBuilder output) {
-     if (input == null) return 0;
-     int state = STATE_BASE;
-     int offset = 0;
-     int startLength = output.length();
-     for (; offset < input.length(); ) {
-       int cp = Character.codePointAt(input, offset);
-       switch (cp) {
-         case '\'':
-           output.append("''");
-           break;
-         case '-':
-         case '+':
-         case '%':
-         case '‰':
-         case '¤':
-           if (state == STATE_BASE) {
-             output.append('\'');
-             output.appendCodePoint(cp);
-             state = STATE_INSIDE_QUOTE;
-           } else {
-             output.appendCodePoint(cp);
-           }
-           break;
+             default:
+                 if (state == STATE_INSIDE_QUOTE) {
+                     output.append('\'');
+                     output.appendCodePoint(cp);
+                     state = STATE_BASE;
+                 } else {
+                     output.appendCodePoint(cp);
+                 }
+                 break;
+             }
+             offset += Character.charCount(cp);
+         }
  
-         default:
-           if (state == STATE_INSIDE_QUOTE) {
+         if (state == STATE_INSIDE_QUOTE) {
              output.append('\'');
-             output.appendCodePoint(cp);
-             state = STATE_BASE;
-           } else {
-             output.appendCodePoint(cp);
-           }
-           break;
-       }
-       offset += Character.charCount(cp);
-     }
+         }
  
-     if (state == STATE_INSIDE_QUOTE) {
-       output.append('\'');
+         return output.length() - startLength;
      }
  
-     return output.length() - startLength;
-   }
-   /** Version of {@link #escape} that returns a String, or null if input is null. */
-   public static String escape(CharSequence input) {
-     if (input == null) return null;
-     StringBuilder sb = new StringBuilder();
-     escape(input, sb);
-     return sb.toString();
-   }
+     /** Version of {@link #escape} that returns a String, or null if input is null. */
+     public static String escape(CharSequence input) {
+         if (input == null)
+             return null;
+         StringBuilder sb = new StringBuilder();
+         escape(input, sb);
+         return sb.toString();
+     }
  
-   public static final NumberFormat.Field getFieldForType(int type) {
-     switch (type) {
-       case TYPE_MINUS_SIGN:
-         return NumberFormat.Field.SIGN;
-       case TYPE_PLUS_SIGN:
-         return NumberFormat.Field.SIGN;
-       case TYPE_PERCENT:
-         return NumberFormat.Field.PERCENT;
-       case TYPE_PERMILLE:
-         return NumberFormat.Field.PERMILLE;
-       case TYPE_CURRENCY_SINGLE:
-         return NumberFormat.Field.CURRENCY;
-       case TYPE_CURRENCY_DOUBLE:
-         return NumberFormat.Field.CURRENCY;
-       case TYPE_CURRENCY_TRIPLE:
-         return NumberFormat.Field.CURRENCY;
-       case TYPE_CURRENCY_QUAD:
-         return NumberFormat.Field.CURRENCY;
-       case TYPE_CURRENCY_QUINT:
-         return NumberFormat.Field.CURRENCY;
-       case TYPE_CURRENCY_OVERFLOW:
-         return NumberFormat.Field.CURRENCY;
-       default:
-         throw new AssertionError();
+     public static final NumberFormat.Field getFieldForType(int type) {
+         switch (type) {
+         case TYPE_MINUS_SIGN:
+             return NumberFormat.Field.SIGN;
+         case TYPE_PLUS_SIGN:
+             return NumberFormat.Field.SIGN;
+         case TYPE_PERCENT:
+             return NumberFormat.Field.PERCENT;
+         case TYPE_PERMILLE:
+             return NumberFormat.Field.PERMILLE;
+         case TYPE_CURRENCY_SINGLE:
+             return NumberFormat.Field.CURRENCY;
+         case TYPE_CURRENCY_DOUBLE:
+             return NumberFormat.Field.CURRENCY;
+         case TYPE_CURRENCY_TRIPLE:
+             return NumberFormat.Field.CURRENCY;
+         case TYPE_CURRENCY_QUAD:
+             return NumberFormat.Field.CURRENCY;
+         case TYPE_CURRENCY_QUINT:
+             return NumberFormat.Field.CURRENCY;
+         case TYPE_CURRENCY_OVERFLOW:
+             return NumberFormat.Field.CURRENCY;
+         default:
+             throw new AssertionError();
+         }
      }
-   }
  
-   /**
-    * Executes the unescape state machine. Replaces the unquoted characters "-", "+", "%", "‰", and
-    * "¤" with the corresponding symbols provided by the {@link SymbolProvider}, and inserts the
-    * result into the NumberStringBuilder at the requested location.
-    *
-    * <p>Example input: "'-'¤x"; example output: "-$x"
-    *
-    * @param affixPattern The original string to be unescaped.
-    * @param output The NumberStringBuilder to mutate with the result.
-    * @param position The index into the NumberStringBuilder to insert the the string.
-    * @param provider An object to generate locale symbols.
-    * @return The length of the string added to affixPattern.
-    */
-   public static int unescape(
-       CharSequence affixPattern,
-       NumberStringBuilder output,
-       int position,
-       SymbolProvider provider) {
-     assert affixPattern != null;
-     int length = 0;
-     long tag = 0L;
-     while (hasNext(tag, affixPattern)) {
-       tag = nextToken(tag, affixPattern);
-       int typeOrCp = getTypeOrCp(tag);
-       if (typeOrCp == TYPE_CURRENCY_OVERFLOW) {
-         // Don't go to the provider for this special case
-         length += output.insertCodePoint(position + length, 0xFFFD, NumberFormat.Field.CURRENCY);
-       } else if (typeOrCp < 0) {
-         length += output.insert(position + length, provider.getSymbol(typeOrCp), getFieldForType(typeOrCp));
-       } else {
-         length += output.insertCodePoint(position + length, typeOrCp, null);
-       }
+     /**
+      * Executes the unescape state machine. Replaces the unquoted characters "-", "+", "%", "‰", and "¤"
+      * with the corresponding symbols provided by the {@link SymbolProvider}, and inserts the result into
+      * the NumberStringBuilder at the requested location.
+      *
+      * <p>
+      * Example input: "'-'¤x"; example output: "-$x"
+      *
+      * @param affixPattern
+      *            The original string to be unescaped.
+      * @param output
+      *            The NumberStringBuilder to mutate with the result.
+      * @param position
+      *            The index into the NumberStringBuilder to insert the the string.
+      * @param provider
+      *            An object to generate locale symbols.
+      * @return The length of the string added to affixPattern.
+      */
+     public static int unescape(
+             CharSequence affixPattern,
+             NumberStringBuilder output,
+             int position,
+             SymbolProvider provider) {
+         assert affixPattern != null;
+         int length = 0;
+         long tag = 0L;
+         while (hasNext(tag, affixPattern)) {
+             tag = nextToken(tag, affixPattern);
+             int typeOrCp = getTypeOrCp(tag);
+             if (typeOrCp == TYPE_CURRENCY_OVERFLOW) {
+                 // Don't go to the provider for this special case
+                 length += output.insertCodePoint(position + length, 0xFFFD, NumberFormat.Field.CURRENCY);
+             } else if (typeOrCp < 0) {
+                 length += output.insert(position + length,
+                         provider.getSymbol(typeOrCp),
+                         getFieldForType(typeOrCp));
+             } else {
+                 length += output.insertCodePoint(position + length, typeOrCp, null);
+             }
+         }
+         return length;
      }
-     return length;
-   }
  
-   /**
-    * Sames as {@link #unescape}, but only calculates the code point count.  More efficient than {@link #unescape}
-    * if you only need the length but not the string itself.
-    *
-    * @param affixPattern The original string to be unescaped.
-    * @param provider An object to generate locale symbols.
-    * @return The number of code points in the unescaped string.
-    */
-   public static int unescapedCodePointCount(CharSequence affixPattern, SymbolProvider provider) {
-     int length = 0;
-     long tag = 0L;
-     while (hasNext(tag, affixPattern)) {
-       tag = nextToken(tag, affixPattern);
-       int typeOrCp = getTypeOrCp(tag);
-       if (typeOrCp == TYPE_CURRENCY_OVERFLOW) {
-         length += 1;
-       } else if (typeOrCp < 0) {
-         CharSequence symbol = provider.getSymbol(typeOrCp);
-         length += Character.codePointCount(symbol, 0, symbol.length());
-       } else {
-         length += 1;
-       }
+     /**
+      * Sames as {@link #unescape}, but only calculates the code point count. More efficient than
+      * {@link #unescape} if you only need the length but not the string itself.
+      *
+      * @param affixPattern
+      *            The original string to be unescaped.
+      * @param provider
+      *            An object to generate locale symbols.
+      * @return The number of code points in the unescaped string.
+      */
+     public static int unescapedCodePointCount(CharSequence affixPattern, SymbolProvider provider) {
+         int length = 0;
+         long tag = 0L;
+         while (hasNext(tag, affixPattern)) {
+             tag = nextToken(tag, affixPattern);
+             int typeOrCp = getTypeOrCp(tag);
+             if (typeOrCp == TYPE_CURRENCY_OVERFLOW) {
+                 length += 1;
+             } else if (typeOrCp < 0) {
+                 CharSequence symbol = provider.getSymbol(typeOrCp);
+                 length += Character.codePointCount(symbol, 0, symbol.length());
+             } else {
+                 length += 1;
+             }
+         }
+         return length;
      }
-     return length;
-   }
  
-   /**
-    * Checks whether the given affix pattern contains at least one token of the given type, which is
-    * one of the constants "TYPE_" in {@link AffixUtils}.
-    *
-    * @param affixPattern The affix pattern to check.
-    * @param type The token type.
-    * @return true if the affix pattern contains the given token type; false otherwise.
-    */
-   public static boolean containsType(CharSequence affixPattern, int type) {
-     if (affixPattern == null || affixPattern.length() == 0) {
+     /**
+      * Checks whether the given affix pattern contains at least one token of the given type, which is one
+      * of the constants "TYPE_" in {@link AffixUtils}.
+      *
+      * @param affixPattern
+      *            The affix pattern to check.
+      * @param type
+      *            The token type.
+      * @return true if the affix pattern contains the given token type; false otherwise.
+      */
+     public static boolean containsType(CharSequence affixPattern, int type) {
+         if (affixPattern == null || affixPattern.length() == 0) {
+             return false;
+         }
+         long tag = 0L;
+         while (hasNext(tag, affixPattern)) {
+             tag = nextToken(tag, affixPattern);
+             if (getTypeOrCp(tag) == type) {
+                 return true;
+             }
+         }
          return false;
      }
-     long tag = 0L;
-     while (hasNext(tag, affixPattern)) {
-       tag = nextToken(tag, affixPattern);
-       if (getTypeOrCp(tag) == type) {
-         return true;
-       }
-     }
-     return false;
-   }
  
-   /**
-    * Checks whether the specified affix pattern has any unquoted currency symbols ("¤").
-    *
-    * @param affixPattern The string to check for currency symbols.
-    * @return true if the literal has at least one unquoted currency symbol; false otherwise.
-    */
-   public static boolean hasCurrencySymbols(CharSequence affixPattern) {
-     if (affixPattern == null || affixPattern.length() == 0) return false;
-     long tag = 0L;
-     while (hasNext(tag, affixPattern)) {
-       tag = nextToken(tag, affixPattern);
-       int typeOrCp = getTypeOrCp(tag);
-       if (typeOrCp < 0 && getFieldForType(typeOrCp) == NumberFormat.Field.CURRENCY) {
-         return true;
-       }
+     /**
+      * Checks whether the specified affix pattern has any unquoted currency symbols ("¤").
+      *
+      * @param affixPattern
+      *            The string to check for currency symbols.
+      * @return true if the literal has at least one unquoted currency symbol; false otherwise.
+      */
+     public static boolean hasCurrencySymbols(CharSequence affixPattern) {
+         if (affixPattern == null || affixPattern.length() == 0)
+             return false;
+         long tag = 0L;
+         while (hasNext(tag, affixPattern)) {
+             tag = nextToken(tag, affixPattern);
+             int typeOrCp = getTypeOrCp(tag);
+             if (typeOrCp < 0 && getFieldForType(typeOrCp) == NumberFormat.Field.CURRENCY) {
+                 return true;
+             }
+         }
+         return false;
      }
-     return false;
-   }
  
-   /**
-    * Replaces all occurrences of tokens with the given type with the given replacement char.
-    *
-    * @param affixPattern The source affix pattern (does not get modified).
-    * @param type The token type.
-    * @param replacementChar The char to substitute in place of chars of the given token type.
-    * @return A string containing the new affix pattern.
-    */
-   public static String replaceType(CharSequence affixPattern, int type, char replacementChar) {
-     if (affixPattern == null || affixPattern.length() == 0) return "";
-     char[] chars = affixPattern.toString().toCharArray();
-     long tag = 0L;
-     while (hasNext(tag, affixPattern)) {
-       tag = nextToken(tag, affixPattern);
-       if (getTypeOrCp(tag) == type) {
-         int offset = getOffset(tag);
-         chars[offset - 1] = replacementChar;
-       }
+     /**
+      * Replaces all occurrences of tokens with the given type with the given replacement char.
+      *
+      * @param affixPattern
+      *            The source affix pattern (does not get modified).
+      * @param type
+      *            The token type.
+      * @param replacementChar
+      *            The char to substitute in place of chars of the given token type.
+      * @return A string containing the new affix pattern.
+      */
+     public static String replaceType(CharSequence affixPattern, int type, char replacementChar) {
+         if (affixPattern == null || affixPattern.length() == 0)
+             return "";
+         char[] chars = affixPattern.toString().toCharArray();
+         long tag = 0L;
+         while (hasNext(tag, affixPattern)) {
+             tag = nextToken(tag, affixPattern);
+             if (getTypeOrCp(tag) == type) {
+                 int offset = getOffset(tag);
+                 chars[offset - 1] = replacementChar;
+             }
+         }
+         return new String(chars);
      }
-     return new String(chars);
-   }
  
-   /**
-    * Returns the next token from the affix pattern.
-    *
-    * @param tag A bitmask used for keeping track of state from token to token. The initial value
-    *     should be 0L.
-    * @param patternString The affix pattern.
-    * @return The bitmask tag to pass to the next call of this method to retrieve the following token
-    *     (never negative), or -1 if there were no more tokens in the affix pattern.
-    * @see #hasNext
-    */
-   public static long nextToken(long tag, CharSequence patternString) {
-     int offset = getOffset(tag);
-     int state = getState(tag);
-     for (; offset < patternString.length(); ) {
-       int cp = Character.codePointAt(patternString, offset);
-       int count = Character.charCount(cp);
-       switch (state) {
-         case STATE_BASE:
-           switch (cp) {
-             case '\'':
-               state = STATE_FIRST_QUOTE;
-               offset += count;
-               // continue to the next code point
-               break;
-             case '-':
-               return makeTag(offset + count, TYPE_MINUS_SIGN, STATE_BASE, 0);
-             case '+':
-               return makeTag(offset + count, TYPE_PLUS_SIGN, STATE_BASE, 0);
-             case '%':
-               return makeTag(offset + count, TYPE_PERCENT, STATE_BASE, 0);
-             case '‰':
-               return makeTag(offset + count, TYPE_PERMILLE, STATE_BASE, 0);
-             case '¤':
-               state = STATE_FIRST_CURR;
-               offset += count;
-               // continue to the next code point
-               break;
 +  /**
 +   * Appends a new affix pattern with all symbols and code points in the given "ignorables" UnicodeSet trimmed from the
 +   * beginning and end. Similar to calling unescape with a symbol provider that always returns the empty string.
 +   *
 +   * <p>
 +   * Accepts and returns a StringBuilder, allocating it only if necessary.
 +   */
 +  public static StringBuilder trimSymbolsAndIgnorables(
 +        CharSequence affixPattern,
 +        UnicodeSet ignorables,
 +        StringBuilder sb) {
 +    assert affixPattern != null;
 +    long tag = 0L;
 +    int trailingIgnorables = 0;
 +    while (hasNext(tag, affixPattern)) {
 +      tag = nextToken(tag, affixPattern);
 +      int typeOrCp = getTypeOrCp(tag);
 +      if (typeOrCp >= 0) {
 +        if (!ignorables.contains(typeOrCp)) {
 +          if (sb == null) {
 +            // Lazy-initialize the StringBuilder
 +            sb = new StringBuilder();
 +          }
 +          sb.appendCodePoint(typeOrCp);
 +          trailingIgnorables = 0;
 +        } else if (sb != null && sb.length() > 0) {
 +          sb.appendCodePoint(typeOrCp);
 +          trailingIgnorables += Character.charCount(typeOrCp);
 +        }
 +      }
 +    }
 +    if (trailingIgnorables > 0) {
 +      sb.setLength(sb.length() - trailingIgnorables);
 +    }
 +    return sb;
 +  }
 +
+     /**
+      * Returns the next token from the affix pattern.
+      *
+      * @param tag
+      *            A bitmask used for keeping track of state from token to token. The initial value should
+      *            be 0L.
+      * @param patternString
+      *            The affix pattern.
+      * @return The bitmask tag to pass to the next call of this method to retrieve the following token
+      *         (never negative), or -1 if there were no more tokens in the affix pattern.
+      * @see #hasNext
+      */
+     public static long nextToken(long tag, CharSequence patternString) {
+         int offset = getOffset(tag);
+         int state = getState(tag);
+         for (; offset < patternString.length();) {
+             int cp = Character.codePointAt(patternString, offset);
+             int count = Character.charCount(cp);
+             switch (state) {
+             case STATE_BASE:
+                 switch (cp) {
+                 case '\'':
+                     state = STATE_FIRST_QUOTE;
+                     offset += count;
+                     // continue to the next code point
+                     break;
+                 case '-':
+                     return makeTag(offset + count, TYPE_MINUS_SIGN, STATE_BASE, 0);
+                 case '+':
+                     return makeTag(offset + count, TYPE_PLUS_SIGN, STATE_BASE, 0);
+                 case '%':
+                     return makeTag(offset + count, TYPE_PERCENT, STATE_BASE, 0);
+                 case '‰':
+                     return makeTag(offset + count, TYPE_PERMILLE, STATE_BASE, 0);
+                 case '¤':
+                     state = STATE_FIRST_CURR;
+                     offset += count;
+                     // continue to the next code point
+                     break;
+                 default:
+                     return makeTag(offset + count, TYPE_CODEPOINT, STATE_BASE, cp);
+                 }
+                 break;
+             case STATE_FIRST_QUOTE:
+                 if (cp == '\'') {
+                     return makeTag(offset + count, TYPE_CODEPOINT, STATE_BASE, cp);
+                 } else {
+                     return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp);
+                 }
+             case STATE_INSIDE_QUOTE:
+                 if (cp == '\'') {
+                     state = STATE_AFTER_QUOTE;
+                     offset += count;
+                     // continue to the next code point
+                     break;
+                 } else {
+                     return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp);
+                 }
+             case STATE_AFTER_QUOTE:
+                 if (cp == '\'') {
+                     return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp);
+                 } else {
+                     state = STATE_BASE;
+                     // re-evaluate this code point
+                     break;
+                 }
+             case STATE_FIRST_CURR:
+                 if (cp == '¤') {
+                     state = STATE_SECOND_CURR;
+                     offset += count;
+                     // continue to the next code point
+                     break;
+                 } else {
+                     return makeTag(offset, TYPE_CURRENCY_SINGLE, STATE_BASE, 0);
+                 }
+             case STATE_SECOND_CURR:
+                 if (cp == '¤') {
+                     state = STATE_THIRD_CURR;
+                     offset += count;
+                     // continue to the next code point
+                     break;
+                 } else {
+                     return makeTag(offset, TYPE_CURRENCY_DOUBLE, STATE_BASE, 0);
+                 }
+             case STATE_THIRD_CURR:
+                 if (cp == '¤') {
+                     state = STATE_FOURTH_CURR;
+                     offset += count;
+                     // continue to the next code point
+                     break;
+                 } else {
+                     return makeTag(offset, TYPE_CURRENCY_TRIPLE, STATE_BASE, 0);
+                 }
+             case STATE_FOURTH_CURR:
+                 if (cp == '¤') {
+                     state = STATE_FIFTH_CURR;
+                     offset += count;
+                     // continue to the next code point
+                     break;
+                 } else {
+                     return makeTag(offset, TYPE_CURRENCY_QUAD, STATE_BASE, 0);
+                 }
+             case STATE_FIFTH_CURR:
+                 if (cp == '¤') {
+                     state = STATE_OVERFLOW_CURR;
+                     offset += count;
+                     // continue to the next code point
+                     break;
+                 } else {
+                     return makeTag(offset, TYPE_CURRENCY_QUINT, STATE_BASE, 0);
+                 }
+             case STATE_OVERFLOW_CURR:
+                 if (cp == '¤') {
+                     offset += count;
+                     // continue to the next code point and loop back to this state
+                     break;
+                 } else {
+                     return makeTag(offset, TYPE_CURRENCY_OVERFLOW, STATE_BASE, 0);
+                 }
              default:
-               return makeTag(offset + count, TYPE_CODEPOINT, STATE_BASE, cp);
-           }
-           break;
+                 throw new AssertionError();
+             }
+         }
+         // End of string
+         switch (state) {
+         case STATE_BASE:
+             // No more tokens in string.
+             return -1L;
          case STATE_FIRST_QUOTE:
-           if (cp == '\'') {
-             return makeTag(offset + count, TYPE_CODEPOINT, STATE_BASE, cp);
-           } else {
-             return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp);
-           }
          case STATE_INSIDE_QUOTE:
-           if (cp == '\'') {
-             state = STATE_AFTER_QUOTE;
-             offset += count;
-             // continue to the next code point
-             break;
-           } else {
-             return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp);
-           }
+             // For consistent behavior with the JDK and ICU 58, throw an exception here.
+             throw new IllegalArgumentException(
+                     "Unterminated quote in pattern affix: \"" + patternString + "\"");
          case STATE_AFTER_QUOTE:
-           if (cp == '\'') {
-             return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp);
-           } else {
-             state = STATE_BASE;
-             // re-evaluate this code point
-             break;
-           }
+             // No more tokens in string.
+             return -1L;
          case STATE_FIRST_CURR:
-           if (cp == '¤') {
-             state = STATE_SECOND_CURR;
-             offset += count;
-             // continue to the next code point
-             break;
-           } else {
              return makeTag(offset, TYPE_CURRENCY_SINGLE, STATE_BASE, 0);
-           }
          case STATE_SECOND_CURR:
-           if (cp == '¤') {
-             state = STATE_THIRD_CURR;
-             offset += count;
-             // continue to the next code point
-             break;
-           } else {
              return makeTag(offset, TYPE_CURRENCY_DOUBLE, STATE_BASE, 0);
-           }
          case STATE_THIRD_CURR:
-           if (cp == '¤') {
-             state = STATE_FOURTH_CURR;
-             offset += count;
-             // continue to the next code point
-             break;
-           } else {
              return makeTag(offset, TYPE_CURRENCY_TRIPLE, STATE_BASE, 0);
-           }
          case STATE_FOURTH_CURR:
-           if (cp == '¤') {
-             state = STATE_FIFTH_CURR;
-             offset += count;
-             // continue to the next code point
-             break;
-           } else {
              return makeTag(offset, TYPE_CURRENCY_QUAD, STATE_BASE, 0);
-           }
          case STATE_FIFTH_CURR:
-           if (cp == '¤') {
-             state = STATE_OVERFLOW_CURR;
-             offset += count;
-             // continue to the next code point
-             break;
-           } else {
              return makeTag(offset, TYPE_CURRENCY_QUINT, STATE_BASE, 0);
-           }
          case STATE_OVERFLOW_CURR:
-           if (cp == '¤') {
-             offset += count;
-             // continue to the next code point and loop back to this state
-             break;
-           } else {
              return makeTag(offset, TYPE_CURRENCY_OVERFLOW, STATE_BASE, 0);
-           }
          default:
-           throw new AssertionError();
-       }
-     }
-     // End of string
-     switch (state) {
-       case STATE_BASE:
-         // No more tokens in string.
-         return -1L;
-       case STATE_FIRST_QUOTE:
-       case STATE_INSIDE_QUOTE:
-         // For consistent behavior with the JDK and ICU 58, throw an exception here.
-         throw new IllegalArgumentException(
-             "Unterminated quote in pattern affix: \"" + patternString + "\"");
-       case STATE_AFTER_QUOTE:
-         // No more tokens in string.
-         return -1L;
-       case STATE_FIRST_CURR:
-         return makeTag(offset, TYPE_CURRENCY_SINGLE, STATE_BASE, 0);
-       case STATE_SECOND_CURR:
-         return makeTag(offset, TYPE_CURRENCY_DOUBLE, STATE_BASE, 0);
-       case STATE_THIRD_CURR:
-         return makeTag(offset, TYPE_CURRENCY_TRIPLE, STATE_BASE, 0);
-       case STATE_FOURTH_CURR:
-         return makeTag(offset, TYPE_CURRENCY_QUAD, STATE_BASE, 0);
-       case STATE_FIFTH_CURR:
-         return makeTag(offset, TYPE_CURRENCY_QUINT, STATE_BASE, 0);
-       case STATE_OVERFLOW_CURR:
-         return makeTag(offset, TYPE_CURRENCY_OVERFLOW, STATE_BASE, 0);
-       default:
-         throw new AssertionError();
+             throw new AssertionError();
+         }
      }
-   }
  
-   /**
-    * Returns whether the affix pattern string has any more tokens to be retrieved from a call to
-    * {@link #nextToken}.
-    *
-    * @param tag The bitmask tag of the previous token, as returned by {@link #nextToken}.
-    * @param string The affix pattern.
-    * @return true if there are more tokens to consume; false otherwise.
-    */
-   public static boolean hasNext(long tag, CharSequence string) {
-     assert tag >= 0;
-     int state = getState(tag);
-     int offset = getOffset(tag);
-     // Special case: the last character in string is an end quote.
-     if (state == STATE_INSIDE_QUOTE
-         && offset == string.length() - 1
-         && string.charAt(offset) == '\'') {
-       return false;
-     } else if (state != STATE_BASE) {
-       return true;
-     } else {
-       return offset < string.length();
+     /**
+      * Returns whether the affix pattern string has any more tokens to be retrieved from a call to
+      * {@link #nextToken}.
+      *
+      * @param tag
+      *            The bitmask tag of the previous token, as returned by {@link #nextToken}.
+      * @param string
+      *            The affix pattern.
+      * @return true if there are more tokens to consume; false otherwise.
+      */
+     public static boolean hasNext(long tag, CharSequence string) {
+         assert tag >= 0;
+         int state = getState(tag);
+         int offset = getOffset(tag);
+         // Special case: the last character in string is an end quote.
+         if (state == STATE_INSIDE_QUOTE
+                 && offset == string.length() - 1
+                 && string.charAt(offset) == '\'') {
+             return false;
+         } else if (state != STATE_BASE) {
+             return true;
+         } else {
+             return offset < string.length();
+         }
      }
-   }
  
-   /**
-    * This function helps determine the identity of the token consumed by {@link #nextToken}.
-    * Converts from a bitmask tag, based on a call to {@link #nextToken}, to its corresponding symbol
-    * type or code point.
-    *
-    * @param tag The bitmask tag of the current token, as returned by {@link #nextToken}.
-    * @return If less than zero, a symbol type corresponding to one of the <code>TYPE_</code>
-    *     constants, such as {@link #TYPE_MINUS_SIGN}. If greater than or equal to zero, a literal
-    *     code point.
-    */
-   public static int getTypeOrCp(long tag) {
-     assert tag >= 0;
-     int type = getType(tag);
-     return (type == TYPE_CODEPOINT) ? getCodePoint(tag) : -type;
-   }
+     /**
+      * This function helps determine the identity of the token consumed by {@link #nextToken}. Converts
+      * from a bitmask tag, based on a call to {@link #nextToken}, to its corresponding symbol type or
+      * code point.
+      *
+      * @param tag
+      *            The bitmask tag of the current token, as returned by {@link #nextToken}.
+      * @return If less than zero, a symbol type corresponding to one of the <code>TYPE_</code> constants,
+      *         such as {@link #TYPE_MINUS_SIGN}. If greater than or equal to zero, a literal code point.
+      */
+     public static int getTypeOrCp(long tag) {
+         assert tag >= 0;
+         int type = getType(tag);
+         return (type == TYPE_CODEPOINT) ? getCodePoint(tag) : -type;
+     }
  
-   /**
-    * Encodes the given values into a 64-bit tag.
-    *
-    * <ul>
-    *   <li>Bits 0-31 => offset (int32)
-    *   <li>Bits 32-35 => type (uint4)
-    *   <li>Bits 36-39 => state (uint4)
-    *   <li>Bits 40-60 => code point (uint21)
-    *   <li>Bits 61-63 => unused
-    * </ul>
-    */
-   private static long makeTag(int offset, int type, int state, int cp) {
-     long tag = 0L;
-     tag |= offset;
-     tag |= (-(long) type) << 32;
-     tag |= ((long) state) << 36;
-     tag |= ((long) cp) << 40;
-     assert tag >= 0;
-     return tag;
-   }
+     /**
+      * Encodes the given values into a 64-bit tag.
+      *
+      * <ul>
+      * <li>Bits 0-31 => offset (int32)
+      * <li>Bits 32-35 => type (uint4)
+      * <li>Bits 36-39 => state (uint4)
+      * <li>Bits 40-60 => code point (uint21)
+      * <li>Bits 61-63 => unused
+      * </ul>
+      */
+     private static long makeTag(int offset, int type, int state, int cp) {
+         long tag = 0L;
+         tag |= offset;
+         tag |= (-(long) type) << 32;
+         tag |= ((long) state) << 36;
+         tag |= ((long) cp) << 40;
+         assert tag >= 0;
+         return tag;
+     }
  
-   static int getOffset(long tag) {
-     return (int) (tag & 0xffffffff);
-   }
+     static int getOffset(long tag) {
+         return (int) (tag & 0xffffffff);
+     }
  
-   static int getType(long tag) {
-     return (int) ((tag >>> 32) & 0xf);
-   }
+     static int getType(long tag) {
+         return (int) ((tag >>> 32) & 0xf);
+     }
  
-   static int getState(long tag) {
-     return (int) ((tag >>> 36) & 0xf);
-   }
+     static int getState(long tag) {
+         return (int) ((tag >>> 36) & 0xf);
+     }
  
-   static int getCodePoint(long tag) {
-     return (int) (tag >>> 40);
-   }
+     static int getCodePoint(long tag) {
+         return (int) (tag >>> 40);
+     }
  }
index a5decc3469f244a230aa711f2eeda722853ffddc,4d34b38133f19401a8bc5d0d5f008f351cb25fef..2f2fad57b72421e078ecc21449e30d16f0ea2163
@@@ -14,172 -14,183 +14,188 @@@ import com.ibm.icu.text.UFieldPosition
   * An interface representing a number to be processed by the decimal formatting pipeline. Includes
   * methods for rounding, plural rules, and decimal digit extraction.
   *
-  * <p>By design, this is NOT IMMUTABLE and NOT THREAD SAFE. It is intended to be an intermediate
-  * object holding state during a pass through the decimal formatting pipeline.
+  * <p>
+  * By design, this is NOT IMMUTABLE and NOT THREAD SAFE. It is intended to be an intermediate object
+  * holding state during a pass through the decimal formatting pipeline.
   *
-  * <p>Implementations of this interface are free to use any internal storage mechanism.
+  * <p>
+  * Implementations of this interface are free to use any internal storage mechanism.
   *
-  * <p>TODO: Should I change this to an abstract class so that logic for min/max digits doesn't need
-  * to be copied to every implementation?
+  * <p>
+  * TODO: Should I change this to an abstract class so that logic for min/max digits doesn't need to be
+  * copied to every implementation?
   */
  public interface DecimalQuantity extends PluralRules.IFixedDecimal {
-   /**
-    * Sets the minimum and maximum integer digits that this {@link DecimalQuantity} should generate.
-    * This method does not perform rounding.
-    *
-    * @param minInt The minimum number of integer digits.
-    * @param maxInt The maximum number of integer digits.
-    */
-   public void setIntegerLength(int minInt, int maxInt);
-   /**
-    * Sets the minimum and maximum fraction digits that this {@link DecimalQuantity} should generate.
-    * This method does not perform rounding.
-    *
-    * @param minFrac The minimum number of fraction digits.
-    * @param maxFrac The maximum number of fraction digits.
-    */
-   public void setFractionLength(int minFrac, int maxFrac);
-   /**
-    * Rounds the number to a specified interval, such as 0.05.
-    *
-    * <p>If rounding to a power of ten, use the more efficient {@link #roundToMagnitude} instead.
-    *
-    * @param roundingInterval The increment to which to round.
-    * @param mathContext The {@link MathContext} to use if rounding is necessary. Undefined behavior
-    *     if null.
-    */
-   public void roundToIncrement(BigDecimal roundingInterval, MathContext mathContext);
-   /**
-    * Rounds the number to a specified magnitude (power of ten).
-    *
-    * @param roundingMagnitude The power of ten to which to round. For example, a value of -2 will
-    *     round to 2 decimal places.
-    * @param mathContext The {@link MathContext} to use if rounding is necessary. Undefined behavior
-    *     if null.
-    */
-   public void roundToMagnitude(int roundingMagnitude, MathContext mathContext);
-   /**
-    * Rounds the number to an infinite number of decimal points. This has no effect except for
-    * forcing the double in {@link DecimalQuantity_AbstractBCD} to adopt its exact representation.
-    */
-   public void roundToInfinity();
-   /**
-    * Truncates the decimals from this DecimalQuantity. Equivalent to calling roundToMagnitude(0, FLOOR)
-    */
-   void truncate();
-   /**
-    * Multiply the internal value.
-    *
-    * @param multiplicand The value by which to multiply.
-    */
-   public void multiplyBy(BigDecimal multiplicand);
-   /**
-    * Scales the number by a power of ten. For example, if the value is currently "1234.56", calling
-    * this method with delta=-3 will change the value to "1.23456".
-    *
-    * @param delta The number of magnitudes of ten to change by.
-    */
-   public void adjustMagnitude(int delta);
-   /**
-    * @return The power of ten corresponding to the most significant nonzero digit.
-    * @throws ArithmeticException If the value represented is zero.
-    */
-   public int getMagnitude() throws ArithmeticException;
-   /** @return Whether the value represented by this {@link DecimalQuantity} is zero. */
-   public boolean isZero();
-   /** @return Whether the value represented by this {@link DecimalQuantity} is less than zero. */
-   public boolean isNegative();
-   /** @return Whether the value represented by this {@link DecimalQuantity} is infinite. */
-   @Override
-   public boolean isInfinite();
-   /** @return Whether the value represented by this {@link DecimalQuantity} is not a number. */
-   @Override
-   public boolean isNaN();
-   /** @return The value contained in this {@link DecimalQuantity} approximated as a double. */
-   public double toDouble();
-   public BigDecimal toBigDecimal();
-   public void setToBigDecimal(BigDecimal input);
-   public int maxRepresentableDigits();
-   // TODO: Should this method be removed, since DecimalQuantity implements IFixedDecimal now?
-   /**
-    * Computes the plural form for this number based on the specified set of rules.
-    *
-    * @param rules A {@link PluralRules} object representing the set of rules.
-    * @return The {@link StandardPlural} according to the PluralRules. If the plural form is not in
-    *     the set of standard plurals, {@link StandardPlural#OTHER} is returned instead.
-    */
-   public StandardPlural getStandardPlural(PluralRules rules);
-   /**
-    * Gets the digit at the specified magnitude. For example, if the represented number is 12.3,
-    * getDigit(-1) returns 3, since 3 is the digit corresponding to 10^-1.
-    *
-    * @param magnitude The magnitude of the digit.
-    * @return The digit at the specified magnitude.
-    */
-   public byte getDigit(int magnitude);
-   /**
-    * Gets the largest power of ten that needs to be displayed. The value returned by this function
-    * will be bounded between minInt and maxInt.
-    *
-    * @return The highest-magnitude digit to be displayed.
-    */
-   public int getUpperDisplayMagnitude();
-   /**
-    * Gets the smallest power of ten that needs to be displayed. The value returned by this function
-    * will be bounded between -minFrac and -maxFrac.
-    *
-    * @return The lowest-magnitude digit to be displayed.
-    */
-   public int getLowerDisplayMagnitude();
-   /**
-    * Returns the string in "plain" format (no exponential notation) using ASCII digits.
-    */
-   public String toPlainString();
-   /**
-    * Like clone, but without the restrictions of the Cloneable interface clone.
-    *
-    * @return A copy of this instance which can be mutated without affecting this instance.
-    */
-   public DecimalQuantity createCopy();
-   /**
-    * Sets this instance to be equal to another instance.
-    *
-    * @param other The instance to copy from.
-    */
-   public void copyFrom(DecimalQuantity other);
-   /** This method is for internal testing only. */
-   public long getPositionFingerprint();
-   /**
-    * If the given {@link FieldPosition} is a {@link UFieldPosition}, populates it with the fraction
-    * length and fraction long value. If the argument is not a {@link UFieldPosition}, nothing
-    * happens.
-    *
-    * @param fp The {@link UFieldPosition} to populate.
-    */
-   public void populateUFieldPosition(FieldPosition fp);
+     /**
+      * Sets the minimum and maximum integer digits that this {@link DecimalQuantity} should generate.
+      * This method does not perform rounding.
+      *
+      * @param minInt
+      *            The minimum number of integer digits.
+      * @param maxInt
+      *            The maximum number of integer digits.
+      */
+     public void setIntegerLength(int minInt, int maxInt);
+     /**
+      * Sets the minimum and maximum fraction digits that this {@link DecimalQuantity} should generate.
+      * This method does not perform rounding.
+      *
+      * @param minFrac
+      *            The minimum number of fraction digits.
+      * @param maxFrac
+      *            The maximum number of fraction digits.
+      */
+     public void setFractionLength(int minFrac, int maxFrac);
+     /**
+      * Rounds the number to a specified interval, such as 0.05.
+      *
+      * <p>
+      * If rounding to a power of ten, use the more efficient {@link #roundToMagnitude} instead.
+      *
+      * @param roundingInterval
+      *            The increment to which to round.
+      * @param mathContext
+      *            The {@link MathContext} to use if rounding is necessary. Undefined behavior if null.
+      */
+     public void roundToIncrement(BigDecimal roundingInterval, MathContext mathContext);
+     /**
+      * Rounds the number to a specified magnitude (power of ten).
+      *
+      * @param roundingMagnitude
+      *            The power of ten to which to round. For example, a value of -2 will round to 2 decimal
+      *            places.
+      * @param mathContext
+      *            The {@link MathContext} to use if rounding is necessary. Undefined behavior if null.
+      */
+     public void roundToMagnitude(int roundingMagnitude, MathContext mathContext);
+     /**
+      * Rounds the number to an infinite number of decimal points. This has no effect except for forcing
+      * the double in {@link DecimalQuantity_AbstractBCD} to adopt its exact representation.
+      */
+     public void roundToInfinity();
++    /**
++     * Truncates the decimals from this DecimalQuantity. Equivalent to calling roundToMagnitude(0, FLOOR)
++     */
++    void truncate();
++
+     /**
+      * Multiply the internal value.
+      *
+      * @param multiplicand
+      *            The value by which to multiply.
+      */
+     public void multiplyBy(BigDecimal multiplicand);
+     /**
+      * Scales the number by a power of ten. For example, if the value is currently "1234.56", calling
+      * this method with delta=-3 will change the value to "1.23456".
+      *
+      * @param delta
+      *            The number of magnitudes of ten to change by.
+      */
+     public void adjustMagnitude(int delta);
+     /**
+      * @return The power of ten corresponding to the most significant nonzero digit.
+      * @throws ArithmeticException
+      *             If the value represented is zero.
+      */
+     public int getMagnitude() throws ArithmeticException;
+     /** @return Whether the value represented by this {@link DecimalQuantity} is zero. */
+     public boolean isZero();
+     /** @return Whether the value represented by this {@link DecimalQuantity} is less than zero. */
+     public boolean isNegative();
+     /** @return Whether the value represented by this {@link DecimalQuantity} is infinite. */
+     @Override
+     public boolean isInfinite();
+     /** @return Whether the value represented by this {@link DecimalQuantity} is not a number. */
+     @Override
+     public boolean isNaN();
+     /** @return The value contained in this {@link DecimalQuantity} approximated as a double. */
+     public double toDouble();
+     public BigDecimal toBigDecimal();
+     public void setToBigDecimal(BigDecimal input);
+     public int maxRepresentableDigits();
+     // TODO: Should this method be removed, since DecimalQuantity implements IFixedDecimal now?
+     /**
+      * Computes the plural form for this number based on the specified set of rules.
+      *
+      * @param rules
+      *            A {@link PluralRules} object representing the set of rules.
+      * @return The {@link StandardPlural} according to the PluralRules. If the plural form is not in the
+      *         set of standard plurals, {@link StandardPlural#OTHER} is returned instead.
+      */
+     public StandardPlural getStandardPlural(PluralRules rules);
+     /**
+      * Gets the digit at the specified magnitude. For example, if the represented number is 12.3,
+      * getDigit(-1) returns 3, since 3 is the digit corresponding to 10^-1.
+      *
+      * @param magnitude
+      *            The magnitude of the digit.
+      * @return The digit at the specified magnitude.
+      */
+     public byte getDigit(int magnitude);
+     /**
+      * Gets the largest power of ten that needs to be displayed. The value returned by this function will
+      * be bounded between minInt and maxInt.
+      *
+      * @return The highest-magnitude digit to be displayed.
+      */
+     public int getUpperDisplayMagnitude();
+     /**
+      * Gets the smallest power of ten that needs to be displayed. The value returned by this function
+      * will be bounded between -minFrac and -maxFrac.
+      *
+      * @return The lowest-magnitude digit to be displayed.
+      */
+     public int getLowerDisplayMagnitude();
+     /**
+      * Returns the string in "plain" format (no exponential notation) using ASCII digits.
+      */
+     public String toPlainString();
+     /**
+      * Like clone, but without the restrictions of the Cloneable interface clone.
+      *
+      * @return A copy of this instance which can be mutated without affecting this instance.
+      */
+     public DecimalQuantity createCopy();
+     /**
+      * Sets this instance to be equal to another instance.
+      *
+      * @param other
+      *            The instance to copy from.
+      */
+     public void copyFrom(DecimalQuantity other);
+     /** This method is for internal testing only. */
+     public long getPositionFingerprint();
+     /**
+      * If the given {@link FieldPosition} is a {@link UFieldPosition}, populates it with the fraction
+      * length and fraction long value. If the argument is not a {@link UFieldPosition}, nothing happens.
+      *
+      * @param fp
+      *            The {@link UFieldPosition} to populate.
+      */
+     public void populateUFieldPosition(FieldPosition fp);
  }
index 147ca5fb628e92d807a0968ad42f14f52e74c07c,a242d7eac00729b60ff94d81234b4d483115fb61..f499e6bb13e3d67bd75dd239c1614a95f2073340
@@@ -19,961 -19,957 +19,1092 @@@ import com.ibm.icu.text.UFieldPosition
   */
  public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
  
-   /**
-    * The power of ten corresponding to the least significant digit in the BCD. For example, if this
-    * object represents the number "3.14", the BCD will be "0x314" and the scale will be -2.
-    *
-    * <p>Note that in {@link java.math.BigDecimal}, the scale is defined differently: the number of
-    * digits after the decimal place, which is the negative of our definition of scale.
-    */
-   protected int scale;
-   /**
-    * The number of digits in the BCD. For example, "1007" has BCD "0x1007" and precision 4. A long
-    * cannot represent precisions greater than 16.
-    *
-    * <p>This value must be re-calculated whenever the value in bcd changes by using {@link
-    * #computePrecisionAndCompact()}.
-    */
-   protected int precision;
-   /**
-    * A bitmask of properties relating to the number represented by this object.
-    *
-    * @see #NEGATIVE_FLAG
-    * @see #INFINITY_FLAG
-    * @see #NAN_FLAG
-    */
-   protected byte flags;
-   protected static final int NEGATIVE_FLAG = 1;
-   protected static final int INFINITY_FLAG = 2;
-   protected static final int NAN_FLAG = 4;
-   // The following three fields relate to the double-to-ascii fast path algorithm.
-   // When a double is given to DecimalQuantityBCD, it is converted to using a fast algorithm. The
-   // fast algorithm guarantees correctness to only the first ~12 digits of the double. The process
-   // of rounding the number ensures that the converted digits are correct, falling back to a slow-
-   // path algorithm if required.  Therefore, if a DecimalQuantity is constructed from a double, it
-   // is *required* that roundToMagnitude(), roundToIncrement(), or roundToInfinity() is called. If
-   // you don't round, assertions will fail in certain other methods if you try calling them.
-   /**
-    * The original number provided by the user and which is represented in BCD. Used when we need to
-    * re-compute the BCD for an exact double representation.
-    */
-   protected double origDouble;
+     /**
+      * The power of ten corresponding to the least significant digit in the BCD. For example, if this
+      * object represents the number "3.14", the BCD will be "0x314" and the scale will be -2.
+      *
+      * <p>
+      * Note that in {@link java.math.BigDecimal}, the scale is defined differently: the number of digits
+      * after the decimal place, which is the negative of our definition of scale.
+      */
+     protected int scale;
+     /**
 -     * The number of digits in the BCD. For example, "1007" has BCD "0x1007" and precision 4. The maximum
 -     * precision is 16 since a long can hold only 16 digits.
++     * The number of digits in the BCD. For example, "1007" has BCD "0x1007" and precision 4. A long
++     * cannot represent precisions greater than 16.
+      *
+      * <p>
+      * This value must be re-calculated whenever the value in bcd changes by using
+      * {@link #computePrecisionAndCompact()}.
+      */
+     protected int precision;
+     /**
+      * A bitmask of properties relating to the number represented by this object.
+      *
+      * @see #NEGATIVE_FLAG
+      * @see #INFINITY_FLAG
+      * @see #NAN_FLAG
+      */
+     protected byte flags;
+     protected static final int NEGATIVE_FLAG = 1;
+     protected static final int INFINITY_FLAG = 2;
+     protected static final int NAN_FLAG = 4;
+     // The following three fields relate to the double-to-ascii fast path algorithm.
+     // When a double is given to DecimalQuantityBCD, it is converted to using a fast algorithm. The
+     // fast algorithm guarantees correctness to only the first ~12 digits of the double. The process
+     // of rounding the number ensures that the converted digits are correct, falling back to a slow-
+     // path algorithm if required. Therefore, if a DecimalQuantity is constructed from a double, it
+     // is *required* that roundToMagnitude(), roundToIncrement(), or roundToInfinity() is called. If
+     // you don't round, assertions will fail in certain other methods if you try calling them.
+     /**
+      * The original number provided by the user and which is represented in BCD. Used when we need to
+      * re-compute the BCD for an exact double representation.
+      */
+     protected double origDouble;
+     /**
+      * The change in magnitude relative to the original double. Used when we need to re-compute the BCD
+      * for an exact double representation.
+      */
+     protected int origDelta;
+     /**
+      * Whether the value in the BCD comes from the double fast path without having been rounded to ensure
+      * correctness
+      */
+     protected boolean isApproximate;
+     // Four positions: left optional '(', left required '[', right required ']', right optional ')'.
+     // These four positions determine which digits are displayed in the output string. They do NOT
+     // affect rounding. These positions are internal-only and can be specified only by the public
+     // endpoints like setFractionLength, setIntegerLength, and setSignificantDigits, among others.
+     //
+     // * Digits between lReqPos and rReqPos are in the "required zone" and are always displayed.
+     // * Digits between lOptPos and rOptPos but outside the required zone are in the "optional zone"
+     // and are displayed unless they are trailing off the left or right edge of the number and
+     // have a numerical value of zero. In order to be "trailing", the digits need to be beyond
+     // the decimal point in their respective directions.
+     // * Digits outside of the "optional zone" are never displayed.
+     //
+     // See the table below for illustrative examples.
+     //
+     // +---------+---------+---------+---------+------------+------------------------+--------------+
+     // | lOptPos | lReqPos | rReqPos | rOptPos |   number   |        positions       | en-US string |
+     // +---------+---------+---------+---------+------------+------------------------+--------------+
+     // |    5    |    2    |   -1    |   -5    |   1234.567 |     ( 12[34.5]67  )    |   1,234.567  |
+     // |    3    |    2    |   -1    |   -5    |   1234.567 |      1(2[34.5]67  )    |     234.567  |
+     // |    3    |    2    |   -1    |   -2    |   1234.567 |      1(2[34.5]6)7      |     234.56   |
+     // |    6    |    4    |    2    |   -5    | 123456789. |  123(45[67]89.     )   | 456,789.     |
+     // |    6    |    4    |    2    |    1    | 123456789. |     123(45[67]8)9.     | 456,780.     |
+     // |   -1    |   -1    |   -3    |   -4    | 0.123456   |     0.1([23]4)56       |        .0234 |
+     // |    6    |    4    |   -2    |   -2    |     12.3   |     (  [  12.3 ])      |    0012.30   |
+     // +---------+---------+---------+---------+------------+------------------------+--------------+
+     //
+     protected int lOptPos = Integer.MAX_VALUE;
+     protected int lReqPos = 0;
+     protected int rReqPos = 0;
+     protected int rOptPos = Integer.MIN_VALUE;
+     @Override
+     public void copyFrom(DecimalQuantity _other) {
+         copyBcdFrom(_other);
+         DecimalQuantity_AbstractBCD other = (DecimalQuantity_AbstractBCD) _other;
+         lOptPos = other.lOptPos;
+         lReqPos = other.lReqPos;
+         rReqPos = other.rReqPos;
+         rOptPos = other.rOptPos;
+         scale = other.scale;
+         precision = other.precision;
+         flags = other.flags;
+         origDouble = other.origDouble;
+         origDelta = other.origDelta;
+         isApproximate = other.isApproximate;
+     }
  
-   /**
-    * The change in magnitude relative to the original double. Used when we need to re-compute the
-    * BCD for an exact double representation.
-    */
-   protected int origDelta;
+     public DecimalQuantity_AbstractBCD clear() {
+         lOptPos = Integer.MAX_VALUE;
+         lReqPos = 0;
+         rReqPos = 0;
+         rOptPos = Integer.MIN_VALUE;
+         flags = 0;
+         setBcdToZero(); // sets scale, precision, hasDouble, origDouble, origDelta, and BCD data
+         return this;
+     }
  
-   /**
-    * Whether the value in the BCD comes from the double fast path without having been rounded to
-    * ensure correctness
-    */
-   protected boolean isApproximate;
-   // Four positions: left optional '(', left required '[', right required ']', right optional ')'.
-   // These four positions determine which digits are displayed in the output string.  They do NOT
-   // affect rounding.  These positions are internal-only and can be specified only by the public
-   // endpoints like setFractionLength, setIntegerLength, and setSignificantDigits, among others.
-   //
-   //   * Digits between lReqPos and rReqPos are in the "required zone" and are always displayed.
-   //   * Digits between lOptPos and rOptPos but outside the required zone are in the "optional zone"
-   //     and are displayed unless they are trailing off the left or right edge of the number and
-   //     have a numerical value of zero.  In order to be "trailing", the digits need to be beyond
-   //     the decimal point in their respective directions.
-   //   * Digits outside of the "optional zone" are never displayed.
-   //
-   // See the table below for illustrative examples.
-   //
-   // +---------+---------+---------+---------+------------+------------------------+--------------+
-   // | lOptPos | lReqPos | rReqPos | rOptPos |   number   |        positions       | en-US string |
-   // +---------+---------+---------+---------+------------+------------------------+--------------+
-   // |    5    |    2    |   -1    |   -5    |   1234.567 |     ( 12[34.5]67  )    |   1,234.567  |
-   // |    3    |    2    |   -1    |   -5    |   1234.567 |      1(2[34.5]67  )    |     234.567  |
-   // |    3    |    2    |   -1    |   -2    |   1234.567 |      1(2[34.5]6)7      |     234.56   |
-   // |    6    |    4    |    2    |   -5    | 123456789. |  123(45[67]89.     )   | 456,789.     |
-   // |    6    |    4    |    2    |    1    | 123456789. |     123(45[67]8)9.     | 456,780.     |
-   // |   -1    |   -1    |   -3    |   -4    | 0.123456   |     0.1([23]4)56       |        .0234 |
-   // |    6    |    4    |   -2    |   -2    |     12.3   |     (  [  12.3 ])      |    0012.30   |
-   // +---------+---------+---------+---------+------------+------------------------+--------------+
-   //
-   protected int lOptPos = Integer.MAX_VALUE;
-   protected int lReqPos = 0;
-   protected int rReqPos = 0;
-   protected int rOptPos = Integer.MIN_VALUE;
+     @Override
+     public void setIntegerLength(int minInt, int maxInt) {
+         // Validation should happen outside of DecimalQuantity, e.g., in the Rounder class.
+         assert minInt >= 0;
+         assert maxInt >= minInt;
  
-   @Override
-   public void copyFrom(DecimalQuantity _other) {
-     copyBcdFrom(_other);
-     DecimalQuantity_AbstractBCD other = (DecimalQuantity_AbstractBCD) _other;
-     lOptPos = other.lOptPos;
-     lReqPos = other.lReqPos;
-     rReqPos = other.rReqPos;
-     rOptPos = other.rOptPos;
-     scale = other.scale;
-     precision = other.precision;
-     flags = other.flags;
-     origDouble = other.origDouble;
-     origDelta = other.origDelta;
-     isApproximate = other.isApproximate;
-   }
+         // Save values into internal state
+         // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
+         lOptPos = maxInt;
+         lReqPos = minInt;
+     }
  
-   public DecimalQuantity_AbstractBCD clear() {
-     lOptPos = Integer.MAX_VALUE;
-     lReqPos = 0;
-     rReqPos = 0;
-     rOptPos = Integer.MIN_VALUE;
-     flags = 0;
-     setBcdToZero(); // sets scale, precision, hasDouble, origDouble, origDelta, and BCD data
-     return this;
-   }
+     @Override
+     public void setFractionLength(int minFrac, int maxFrac) {
+         // Validation should happen outside of DecimalQuantity, e.g., in the Rounder class.
+         assert minFrac >= 0;
+         assert maxFrac >= minFrac;
  
-   @Override
-   public void setIntegerLength(int minInt, int maxInt) {
-     // Validation should happen outside of DecimalQuantity, e.g., in the Rounder class.
-     assert minInt >= 0;
-     assert maxInt >= minInt;
-     // Save values into internal state
-     // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
-     lOptPos = maxInt;
-     lReqPos = minInt;
-   }
-   @Override
-   public void setFractionLength(int minFrac, int maxFrac) {
-     // Validation should happen outside of DecimalQuantity, e.g., in the Rounder class.
-     assert minFrac >= 0;
-     assert maxFrac >= minFrac;
-     // Save values into internal state
-     // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
-     rReqPos = -minFrac;
-     rOptPos = -maxFrac;
-   }
+         // Save values into internal state
+         // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE
+         rReqPos = -minFrac;
+         rOptPos = -maxFrac;
+     }
  
-   @Override
-   public long getPositionFingerprint() {
-     long fingerprint = 0;
-     fingerprint ^= lOptPos;
-     fingerprint ^= (lReqPos << 16);
-     fingerprint ^= ((long) rReqPos << 32);
-     fingerprint ^= ((long) rOptPos << 48);
-     return fingerprint;
-   }
+     @Override
+     public long getPositionFingerprint() {
+         long fingerprint = 0;
+         fingerprint ^= lOptPos;
+         fingerprint ^= (lReqPos << 16);
+         fingerprint ^= ((long) rReqPos << 32);
+         fingerprint ^= ((long) rOptPos << 48);
+         return fingerprint;
+     }
  
-   @Override
-   public void roundToIncrement(BigDecimal roundingIncrement, MathContext mathContext) {
-     // TODO: Avoid converting back and forth to BigDecimal.
-     BigDecimal temp = toBigDecimal();
-     temp =
-         temp.divide(roundingIncrement, 0, mathContext.getRoundingMode())
-             .multiply(roundingIncrement)
-             .round(mathContext);
-     if (temp.signum() == 0) {
-       setBcdToZero(); // keeps negative flag for -0.0
-     } else {
-       setToBigDecimal(temp);
+     @Override
+     public void roundToIncrement(BigDecimal roundingIncrement, MathContext mathContext) {
+         // TODO: Avoid converting back and forth to BigDecimal.
+         BigDecimal temp = toBigDecimal();
+         temp = temp.divide(roundingIncrement, 0, mathContext.getRoundingMode())
+                 .multiply(roundingIncrement).round(mathContext);
+         if (temp.signum() == 0) {
+             setBcdToZero(); // keeps negative flag for -0.0
+         } else {
+             setToBigDecimal(temp);
+         }
      }
-   }
  
-   @Override
-   public void multiplyBy(BigDecimal multiplicand) {
-     if (isInfinite() || isZero() || isNaN()) {
-       return;
+     @Override
+     public void multiplyBy(BigDecimal multiplicand) {
+         if (isInfinite() || isZero() || isNaN()) {
+             return;
+         }
+         BigDecimal temp = toBigDecimal();
+         temp = temp.multiply(multiplicand);
+         setToBigDecimal(temp);
      }
-     BigDecimal temp = toBigDecimal();
-     temp = temp.multiply(multiplicand);
-     setToBigDecimal(temp);
-   }
  
-   @Override
-   public int getMagnitude() throws ArithmeticException {
-     if (precision == 0) {
-       throw new ArithmeticException("Magnitude is not well-defined for zero");
-     } else {
-       return scale + precision - 1;
+     @Override
+     public int getMagnitude() throws ArithmeticException {
+         if (precision == 0) {
+             throw new ArithmeticException("Magnitude is not well-defined for zero");
+         } else {
+             return scale + precision - 1;
+         }
      }
-   }
  
++<<<<<<< .working
 +  @Override
 +  public void adjustMagnitude(int delta) {
 +    if (precision != 0) {
 +      // TODO: Math.addExact is not in 1.6 or 1.7
 +      scale = Math.addExact(scale, delta);
 +      origDelta = Math.addExact(origDelta, delta);
++=======
+     @Override
+     public void adjustMagnitude(int delta) {
+         if (precision != 0) {
+             scale += delta;
+             origDelta += delta;
+         }
++>>>>>>> .merge-right.r40750
      }
-   }
  
-   @Override
-   public StandardPlural getStandardPlural(PluralRules rules) {
-     if (rules == null) {
-       // Fail gracefully if the user didn't provide a PluralRules
-       return StandardPlural.OTHER;
-     } else {
-       @SuppressWarnings("deprecation")
-       String ruleString = rules.select(this);
-       return StandardPlural.orOtherFromString(ruleString);
+     @Override
+     public StandardPlural getStandardPlural(PluralRules rules) {
+         if (rules == null) {
+             // Fail gracefully if the user didn't provide a PluralRules
+             return StandardPlural.OTHER;
+         } else {
+             @SuppressWarnings("deprecation")
+             String ruleString = rules.select(this);
+             return StandardPlural.orOtherFromString(ruleString);
+         }
      }
-   }
  
-   @Override
-   public double getPluralOperand(Operand operand) {
-     // If this assertion fails, you need to call roundToInfinity() or some other rounding method.
-     // See the comment at the top of this file explaining the "isApproximate" field.
-     assert !isApproximate;
-     switch (operand) {
-       case i:
-         return toLong();
-       case f:
-         return toFractionLong(true);
-       case t:
-         return toFractionLong(false);
-       case v:
-         return fractionCount();
-       case w:
-         return fractionCountWithoutTrailingZeros();
-       default:
-         return Math.abs(toDouble());
+     @Override
+     public double getPluralOperand(Operand operand) {
+         // If this assertion fails, you need to call roundToInfinity() or some other rounding method.
+         // See the comment at the top of this file explaining the "isApproximate" field.
+         assert !isApproximate;
+         switch (operand) {
+         case i:
+             return toLong();
+         case f:
+             return toFractionLong(true);
+         case t:
+             return toFractionLong(false);
+         case v:
+             return fractionCount();
+         case w:
+             return fractionCountWithoutTrailingZeros();
+         default:
+             return Math.abs(toDouble());
+         }
      }
-   }
  
-   @Override
-   public void populateUFieldPosition(FieldPosition fp) {
-     if (fp instanceof UFieldPosition) {
-       ((UFieldPosition) fp)
-           .setFractionDigits((int) getPluralOperand(Operand.v), (long) getPluralOperand(Operand.f));
+     @Override
+     public void populateUFieldPosition(FieldPosition fp) {
+         if (fp instanceof UFieldPosition) {
+             ((UFieldPosition) fp).setFractionDigits((int) getPluralOperand(Operand.v),
+                     (long) getPluralOperand(Operand.f));
+         }
      }
-   }
  
-   @Override
-   public int getUpperDisplayMagnitude() {
-     // If this assertion fails, you need to call roundToInfinity() or some other rounding method.
-     // See the comment at the top of this file explaining the "isApproximate" field.
-     assert !isApproximate;
-     int magnitude = scale + precision;
-     int result = (lReqPos > magnitude) ? lReqPos : (lOptPos < magnitude) ? lOptPos : magnitude;
-     return result - 1;
-   }
-   @Override
-   public int getLowerDisplayMagnitude() {
-     // If this assertion fails, you need to call roundToInfinity() or some other rounding method.
-     // See the comment at the top of this file explaining the "isApproximate" field.
-     assert !isApproximate;
-     int magnitude = scale;
-     int result = (rReqPos < magnitude) ? rReqPos : (rOptPos > magnitude) ? rOptPos : magnitude;
-     return result;
-   }
+     @Override
+     public int getUpperDisplayMagnitude() {
+         // If this assertion fails, you need to call roundToInfinity() or some other rounding method.
+         // See the comment at the top of this file explaining the "isApproximate" field.
+         assert !isApproximate;
  
-   @Override
-   public byte getDigit(int magnitude) {
-     // If this assertion fails, you need to call roundToInfinity() or some other rounding method.
-     // See the comment at the top of this file explaining the "isApproximate" field.
-     assert !isApproximate;
+         int magnitude = scale + precision;
+         int result = (lReqPos > magnitude) ? lReqPos : (lOptPos < magnitude) ? lOptPos : magnitude;
+         return result - 1;
+     }
  
-     return getDigitPos(magnitude - scale);
-   }
+     @Override
+     public int getLowerDisplayMagnitude() {
+         // If this assertion fails, you need to call roundToInfinity() or some other rounding method.
+         // See the comment at the top of this file explaining the "isApproximate" field.
+         assert !isApproximate;
  
-   private int fractionCount() {
-     return -getLowerDisplayMagnitude();
-   }
+         int magnitude = scale;
+         int result = (rReqPos < magnitude) ? rReqPos : (rOptPos > magnitude) ? rOptPos : magnitude;
+         return result;
+     }
  
-   private int fractionCountWithoutTrailingZeros() {
-     return Math.max(-scale, 0);
-   }
+     @Override
+     public byte getDigit(int magnitude) {
+         // If this assertion fails, you need to call roundToInfinity() or some other rounding method.
+         // See the comment at the top of this file explaining the "isApproximate" field.
+         assert !isApproximate;
  
-   @Override
-   public boolean isNegative() {
-     return (flags & NEGATIVE_FLAG) != 0;
-   }
+         return getDigitPos(magnitude - scale);
+     }
  
-   @Override
-   public boolean isInfinite() {
-     return (flags & INFINITY_FLAG) != 0;
-   }
+     private int fractionCount() {
+         return -getLowerDisplayMagnitude();
+     }
  
-   @Override
-   public boolean isNaN() {
-     return (flags & NAN_FLAG) != 0;
-   }
+     private int fractionCountWithoutTrailingZeros() {
+         return Math.max(-scale, 0);
+     }
  
-   @Override
-   public boolean isZero() {
-     return precision == 0;
-   }
+     @Override
+     public boolean isNegative() {
+         return (flags & NEGATIVE_FLAG) != 0;
+     }
  
-   public void setToInt(int n) {
-     setBcdToZero();
-     flags = 0;
-     if (n < 0) {
-       flags |= NEGATIVE_FLAG;
-       n = -n;
+     @Override
+     public boolean isInfinite() {
+         return (flags & INFINITY_FLAG) != 0;
      }
-     if (n != 0) {
-       _setToInt(n);
-       compact();
+     @Override
+     public boolean isNaN() {
+         return (flags & NAN_FLAG) != 0;
      }
-   }
  
-   private void _setToInt(int n) {
-     if (n == Integer.MIN_VALUE) {
-       readLongToBcd(-(long) n);
-     } else {
-       readIntToBcd(n);
+     @Override
+     public boolean isZero() {
+         return precision == 0;
      }
-   }
  
-   public void setToLong(long n) {
-     setBcdToZero();
-     flags = 0;
-     if (n < 0) {
-       flags |= NEGATIVE_FLAG;
-       n = -n;
+     public void setToInt(int n) {
+         setBcdToZero();
+         flags = 0;
+         if (n < 0) {
+             flags |= NEGATIVE_FLAG;
+             n = -n;
+         }
+         if (n != 0) {
+             _setToInt(n);
+             compact();
+         }
      }
-     if (n != 0) {
-       _setToLong(n);
-       compact();
+     private void _setToInt(int n) {
+         if (n == Integer.MIN_VALUE) {
+             readLongToBcd(-(long) n);
+         } else {
+             readIntToBcd(n);
+         }
      }
-   }
  
-   private void _setToLong(long n) {
-     if (n == Long.MIN_VALUE) {
-       readBigIntegerToBcd(BigInteger.valueOf(n).negate());
-     } else if (n <= Integer.MAX_VALUE) {
-       readIntToBcd((int) n);
-     } else {
-       readLongToBcd(n);
+     public void setToLong(long n) {
+         setBcdToZero();
+         flags = 0;
+         if (n < 0) {
+             flags |= NEGATIVE_FLAG;
+             n = -n;
+         }
+         if (n != 0) {
+             _setToLong(n);
+             compact();
+         }
      }
-   }
  
-   public void setToBigInteger(BigInteger n) {
-     setBcdToZero();
-     flags = 0;
-     if (n.signum() == -1) {
-       flags |= NEGATIVE_FLAG;
-       n = n.negate();
+     private void _setToLong(long n) {
+         if (n == Long.MIN_VALUE) {
+             readBigIntegerToBcd(BigInteger.valueOf(n).negate());
+         } else if (n <= Integer.MAX_VALUE) {
+             readIntToBcd((int) n);
+         } else {
+             readLongToBcd(n);
+         }
      }
-     if (n.signum() != 0) {
-       _setToBigInteger(n);
-       compact();
+     public void setToBigInteger(BigInteger n) {
+         setBcdToZero();
+         flags = 0;
+         if (n.signum() == -1) {
+             flags |= NEGATIVE_FLAG;
+             n = n.negate();
+         }
+         if (n.signum() != 0) {
+             _setToBigInteger(n);
+             compact();
+         }
      }
-   }
  
-   private void _setToBigInteger(BigInteger n) {
-     if (n.bitLength() < 32) {
-       readIntToBcd(n.intValue());
-     } else if (n.bitLength() < 64) {
-       readLongToBcd(n.longValue());
-     } else {
-       readBigIntegerToBcd(n);
+     private void _setToBigInteger(BigInteger n) {
+         if (n.bitLength() < 32) {
+             readIntToBcd(n.intValue());
+         } else if (n.bitLength() < 64) {
+             readLongToBcd(n.longValue());
+         } else {
+             readBigIntegerToBcd(n);
+         }
      }
-   }
  
-   /**
-    * Sets the internal BCD state to represent the value in the given double.
-    *
-    * @param n The value to consume.
-    */
-   public void setToDouble(double n) {
-     setBcdToZero();
-     flags = 0;
-     // Double.compare() handles +0.0 vs -0.0
-     if (Double.compare(n, 0.0) < 0) {
-       flags |= NEGATIVE_FLAG;
-       n = -n;
-     }
-     if (Double.isNaN(n)) {
-       flags |= NAN_FLAG;
-     } else if (Double.isInfinite(n)) {
-       flags |= INFINITY_FLAG;
-     } else if (n != 0) {
-       _setToDoubleFast(n);
-       compact();
+     /**
+      * Sets the internal BCD state to represent the value in the given double.
+      *
+      * @param n
+      *            The value to consume.
+      */
+     public void setToDouble(double n) {
+         setBcdToZero();
+         flags = 0;
+         // Double.compare() handles +0.0 vs -0.0
+         if (Double.compare(n, 0.0) < 0) {
+             flags |= NEGATIVE_FLAG;
+             n = -n;
+         }
+         if (Double.isNaN(n)) {
+             flags |= NAN_FLAG;
+         } else if (Double.isInfinite(n)) {
+             flags |= INFINITY_FLAG;
+         } else if (n != 0) {
+             _setToDoubleFast(n);
+             compact();
+         }
      }
-   }
  
-   private static final double[] DOUBLE_MULTIPLIERS = {
-     1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16,
-     1e17, 1e18, 1e19, 1e20, 1e21
-   };
+     private static final double[] DOUBLE_MULTIPLIERS = {
+             1e0,
+             1e1,
+             1e2,
+             1e3,
+             1e4,
+             1e5,
+             1e6,
+             1e7,
+             1e8,
+             1e9,
+             1e10,
+             1e11,
+             1e12,
+             1e13,
+             1e14,
+             1e15,
+             1e16,
+             1e17,
+             1e18,
+             1e19,
+             1e20,
+             1e21 };
+     /**
+      * Uses double multiplication and division to get the number into integer space before converting to
+      * digits. Since double arithmetic is inexact, the resulting digits may not be accurate.
+      */
+     private void _setToDoubleFast(double n) {
+         isApproximate = true;
+         origDouble = n;
+         origDelta = 0;
+         // NOTE: Unlike ICU4C, doubles are always IEEE 754 doubles.
+         long ieeeBits = Double.doubleToLongBits(n);
+         int exponent = (int) ((ieeeBits & 0x7ff0000000000000L) >> 52) - 0x3ff;
  
-   /**
-    * Uses double multiplication and division to get the number into integer space before converting
-    * to digits. Since double arithmetic is inexact, the resulting digits may not be accurate.
-    */
-   private void _setToDoubleFast(double n) {
-     isApproximate = true;
-     origDouble = n;
-     origDelta = 0;
-     // NOTE: Unlike ICU4C, doubles are always IEEE 754 doubles.
-     long ieeeBits = Double.doubleToLongBits(n);
-     int exponent = (int) ((ieeeBits & 0x7ff0000000000000L) >> 52) - 0x3ff;
-     // Not all integers can be represented exactly for exponent > 52
-     if (exponent <= 52 && (long) n == n) {
-       _setToLong((long) n);
-       return;
-     }
-     // 3.3219... is log2(10)
-     int fracLength = (int) ((52 - exponent) / 3.32192809489);
-     if (fracLength >= 0) {
-       int i = fracLength;
-       // 1e22 is the largest exact double.
-       for (; i >= 22; i -= 22) n *= 1e22;
-       n *= DOUBLE_MULTIPLIERS[i];
-     } else {
-       int i = fracLength;
-       // 1e22 is the largest exact double.
-       for (; i <= -22; i += 22) n /= 1e22;
-       n /= DOUBLE_MULTIPLIERS[-i];
-     }
-     long result = Math.round(n);
-     if (result != 0) {
-       _setToLong(result);
-       scale -= fracLength;
+         // Not all integers can be represented exactly for exponent > 52
+         if (exponent <= 52 && (long) n == n) {
+             _setToLong((long) n);
+             return;
+         }
+         // 3.3219... is log2(10)
+         int fracLength = (int) ((52 - exponent) / 3.32192809489);
+         if (fracLength >= 0) {
+             int i = fracLength;
+             // 1e22 is the largest exact double.
+             for (; i >= 22; i -= 22)
+                 n *= 1e22;
+             n *= DOUBLE_MULTIPLIERS[i];
+         } else {
+             int i = fracLength;
+             // 1e22 is the largest exact double.
+             for (; i <= -22; i += 22)
+                 n /= 1e22;
+             n /= DOUBLE_MULTIPLIERS[-i];
+         }
+         long result = Math.round(n);
+         if (result != 0) {
+             _setToLong(result);
+             scale -= fracLength;
+         }
      }
-   }
  
-   /**
-    * Uses Double.toString() to obtain an exact accurate representation of the double, overwriting it
-    * into the BCD. This method can be called at any point after {@link #_setToDoubleFast} while
-    * {@link #isApproximate} is still true.
-    */
-   private void convertToAccurateDouble() {
-     double n = origDouble;
-     assert n != 0;
-     int delta = origDelta;
-     setBcdToZero();
-     // Call the slow oracle function (Double.toString in Java, sprintf in C++).
-     String dstr = Double.toString(n);
-     if (dstr.indexOf('E') != -1) {
-       // Case 1: Exponential notation.
-       assert dstr.indexOf('.') == 1;
-       int expPos = dstr.indexOf('E');
-       _setToLong(Long.parseLong(dstr.charAt(0) + dstr.substring(2, expPos)));
-       scale += Integer.parseInt(dstr.substring(expPos + 1)) - (expPos - 1) + 1;
-     } else if (dstr.charAt(0) == '0') {
-       // Case 2: Fraction-only number.
-       assert dstr.indexOf('.') == 1;
-       _setToLong(Long.parseLong(dstr.substring(2)));
-       scale += 2 - dstr.length();
-     } else if (dstr.charAt(dstr.length() - 1) == '0') {
-       // Case 3: Integer-only number.
-       // Note: this path should not normally happen, because integer-only numbers are captured
-       // before the approximate double logic is performed.
-       assert dstr.indexOf('.') == dstr.length() - 2;
-       assert dstr.length() - 2 <= 18;
-       _setToLong(Long.parseLong(dstr.substring(0, dstr.length() - 2)));
-       // no need to adjust scale
-     } else {
-       // Case 4: Number with both a fraction and an integer.
-       int decimalPos = dstr.indexOf('.');
-       _setToLong(Long.parseLong(dstr.substring(0, decimalPos) + dstr.substring(decimalPos + 1)));
-       scale += decimalPos - dstr.length() + 1;
-     }
-     scale += delta;
-     compact();
-     explicitExactDouble = true;
-   }
+     /**
+      * Uses Double.toString() to obtain an exact accurate representation of the double, overwriting it
+      * into the BCD. This method can be called at any point after {@link #_setToDoubleFast} while
+      * {@link #isApproximate} is still true.
+      */
+     private void convertToAccurateDouble() {
+         double n = origDouble;
+         assert n != 0;
+         int delta = origDelta;
+         setBcdToZero();
  
-   /**
-    * Whether this {@link DecimalQuantity_DualStorageBCD} has been explicitly converted to an exact double. true if
-    * backed by a double that was explicitly converted via convertToAccurateDouble; false otherwise.
-    * Used for testing.
-    *
-    * @internal
-    * @deprecated This API is ICU internal only.
-    */
-   @Deprecated public boolean explicitExactDouble = false;
+         // Call the slow oracle function (Double.toString in Java, sprintf in C++).
+         String dstr = Double.toString(n);
+         if (dstr.indexOf('E') != -1) {
+             // Case 1: Exponential notation.
+             assert dstr.indexOf('.') == 1;
+             int expPos = dstr.indexOf('E');
+             _setToLong(Long.parseLong(dstr.charAt(0) + dstr.substring(2, expPos)));
+             scale += Integer.parseInt(dstr.substring(expPos + 1)) - (expPos - 1) + 1;
+         } else if (dstr.charAt(0) == '0') {
+             // Case 2: Fraction-only number.
+             assert dstr.indexOf('.') == 1;
+             _setToLong(Long.parseLong(dstr.substring(2)));
+             scale += 2 - dstr.length();
+         } else if (dstr.charAt(dstr.length() - 1) == '0') {
+             // Case 3: Integer-only number.
+             // Note: this path should not normally happen, because integer-only numbers are captured
+             // before the approximate double logic is performed.
+             assert dstr.indexOf('.') == dstr.length() - 2;
+             assert dstr.length() - 2 <= 18;
+             _setToLong(Long.parseLong(dstr.substring(0, dstr.length() - 2)));
+             // no need to adjust scale
+         } else {
+             // Case 4: Number with both a fraction and an integer.
+             int decimalPos = dstr.indexOf('.');
+             _setToLong(Long.parseLong(dstr.substring(0, decimalPos) + dstr.substring(decimalPos + 1)));
+             scale += decimalPos - dstr.length() + 1;
+         }
  
-   /**
-    * Sets the internal BCD state to represent the value in the given BigDecimal.
-    *
-    * @param n The value to consume.
-    */
-   @Override
-   public void setToBigDecimal(BigDecimal n) {
-     setBcdToZero();
-     flags = 0;
-     if (n.signum() == -1) {
-       flags |= NEGATIVE_FLAG;
-       n = n.negate();
+         scale += delta;
+         compact();
+         explicitExactDouble = true;
      }
-     if (n.signum() != 0) {
-       _setToBigDecimal(n);
-       compact();
+     /**
+      * Whether this {@link DecimalQuantity_DualStorageBCD} has been explicitly converted to an exact
+      * double. true if backed by a double that was explicitly converted via convertToAccurateDouble;
+      * false otherwise. Used for testing.
+      *
+      * @internal
+      * @deprecated This API is ICU internal only.
+      */
+     @Deprecated
+     public boolean explicitExactDouble = false;
+     /**
+      * Sets the internal BCD state to represent the value in the given BigDecimal.
+      *
+      * @param n
+      *            The value to consume.
+      */
+     @Override
+     public void setToBigDecimal(BigDecimal n) {
+         setBcdToZero();
+         flags = 0;
+         if (n.signum() == -1) {
+             flags |= NEGATIVE_FLAG;
+             n = n.negate();
+         }
+         if (n.signum() != 0) {
+             _setToBigDecimal(n);
+             compact();
+         }
      }
-   }
  
++<<<<<<< .working
 +  private void _setToBigDecimal(BigDecimal n) {
 +    int fracLength = n.scale();
 +    n = n.scaleByPowerOfTen(fracLength);
 +    BigInteger bi = n.toBigInteger();
 +    _setToBigInteger(bi);
 +    scale -= fracLength;
 +  }
 +
 +  /**
 +   * Returns a long approximating the internal BCD. A long can only represent the integral part of
 +   * the number.
 +   *
 +   * @return A double representation of the internal BCD.
 +   */
 +  public long toLong() {
 +    long result = 0L;
 +    for (int magnitude = scale + precision - 1; magnitude >= 0; magnitude--) {
 +      result = result * 10 + getDigitPos(magnitude - scale);
++=======
+     private void _setToBigDecimal(BigDecimal n) {
+         int fracLength = n.scale();
+         n = n.scaleByPowerOfTen(fracLength);
+         BigInteger bi = n.toBigInteger();
+         _setToBigInteger(bi);
+         scale -= fracLength;
++>>>>>>> .merge-right.r40750
      }
-     return result;
-   }
  
++<<<<<<< .working
 +  /**
 +   * This returns a long representing the fraction digits of the number, as required by PluralRules.
 +   * For example, if we represent the number "1.20" (including optional and required digits), then
 +   * this function returns "20" if includeTrailingZeros is true or "2" if false.
 +   */
 +  public long toFractionLong(boolean includeTrailingZeros) {
 +    long result = 0L;
 +    int magnitude = -1;
 +    for (;
 +        (magnitude >= scale || (includeTrailingZeros && magnitude >= rReqPos))
 +            && magnitude >= rOptPos;
 +        magnitude--) {
 +      result = result * 10 + getDigitPos(magnitude - scale);
++=======
+     /**
+      * Returns a long approximating the internal BCD. A long can only represent the integral part of the
+      * number.
+      *
+      * @return A double representation of the internal BCD.
+      */
+     protected long toLong() {
+         long result = 0L;
+         for (int magnitude = scale + precision - 1; magnitude >= 0; magnitude--) {
+             result = result * 10 + getDigitPos(magnitude - scale);
+         }
+         return result;
++>>>>>>> .merge-right.r40750
      }
-     return result;
-   }
  
++<<<<<<< .working
 +  static final byte[] INT64_BCD = {9,2,2,3,3,7,2,0,3,6,8,5,4,7,7,5,8,0,7};
 +
 +  /**
 +   * Returns whether or not a Long can fully represent the value stored in this DecimalQuantity.
 +   * Assumes that the DecimalQuantity is positive.
 +   */
 +  public boolean fitsInLong() {
 +      if (isZero()) {
 +          return true;
 +      }
 +      if (scale < 0) {
 +          return false;
 +      }
 +      int magnitude = getMagnitude();
 +      if (magnitude < 18) {
 +          return true;
 +      }
 +      if (magnitude > 18) {
 +          return false;
 +      }
 +      // Hard case: the magnitude is 10^18.
 +      // The largest int64 is: 9,223,372,036,854,775,807
 +      for (int p=0; p<precision; p++) {
 +          byte digit = getDigitPos(18-p);
 +          if (digit < INT64_BCD[p]) {
 +              return true;
 +          } else if (digit > INT64_BCD[p]) {
 +              return false;
 +          }
 +      }
 +      // Exactly equal to max long.
 +      return true;
 +  }
 +
 +  /**
 +   * Returns a double approximating the internal BCD. The double may not retain all of the
 +   * information encoded in the BCD if the BCD represents a number out of range of a double.
 +   *
 +   * @return A double representation of the internal BCD.
 +   */
 +  @Override
 +  public double toDouble() {
 +    if (isApproximate) {
 +      return toDoubleFromOriginal();
++=======
+     /**
+      * This returns a long representing the fraction digits of the number, as required by PluralRules.
+      * For example, if we represent the number "1.20" (including optional and required digits), then this
+      * function returns "20" if includeTrailingZeros is true or "2" if false.
+      */
+     protected long toFractionLong(boolean includeTrailingZeros) {
+         long result = 0L;
+         int magnitude = -1;
+         for (; (magnitude >= scale || (includeTrailingZeros && magnitude >= rReqPos))
+                 && magnitude >= rOptPos; magnitude--) {
+             result = result * 10 + getDigitPos(magnitude - scale);
+         }
+         return result;
++>>>>>>> .merge-right.r40750
      }
  
-     if (isNaN()) {
-       return Double.NaN;
-     } else if (isInfinite()) {
-       return isNegative() ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
-     }
-     long tempLong = 0L;
-     int lostDigits = precision - Math.min(precision, 17);
-     for (int shift = precision - 1; shift >= lostDigits; shift--) {
-       tempLong = tempLong * 10 + getDigitPos(shift);
-     }
-     double result = tempLong;
-     int _scale = scale + lostDigits;
-     if (_scale >= 0) {
-       // 1e22 is the largest exact double.
-       int i = _scale;
-       for (; i >= 22; i -= 22) result *= 1e22;
-       result *= DOUBLE_MULTIPLIERS[i];
-     } else {
-       // 1e22 is the largest exact double.
-       int i = _scale;
-       for (; i <= -22; i += 22) result /= 1e22;
-       result /= DOUBLE_MULTIPLIERS[-i];
-     }
-     if (isNegative()) result = -result;
-     return result;
-   }
-   @Override
-   public BigDecimal toBigDecimal() {
-     if (isApproximate) {
-       // Converting to a BigDecimal requires Double.toString().
-       convertToAccurateDouble();
-     }
-     return bcdToBigDecimal();
-   }
-   protected double toDoubleFromOriginal() {
-     double result = origDouble;
-     int delta = origDelta;
-     if (delta >= 0) {
-       // 1e22 is the largest exact double.
-       for (; delta >= 22; delta -= 22) result *= 1e22;
-       result *= DOUBLE_MULTIPLIERS[delta];
-     } else {
-       // 1e22 is the largest exact double.
-       for (; delta <= -22; delta += 22) result /= 1e22;
-       result /= DOUBLE_MULTIPLIERS[-delta];
-     }
-     if (isNegative()) result *= -1;
-     return result;
-   }
-   private static int safeSubtract(int a, int b) {
-     int diff = a - b;
-     if (b < 0 && diff < a) return Integer.MAX_VALUE;
-     if (b > 0 && diff > a) return Integer.MIN_VALUE;
-     return diff;
-   }
+     /**
+      * Returns a double approximating the internal BCD. The double may not retain all of the information
+      * encoded in the BCD if the BCD represents a number out of range of a double.
+      *
+      * @return A double representation of the internal BCD.
+      */
+     @Override
+     public double toDouble() {
+         if (isApproximate) {
+             return toDoubleFromOriginal();
+         }
  
-   private static final int SECTION_LOWER_EDGE = -1;
-   private static final int SECTION_UPPER_EDGE = -2;
+         if (isNaN()) {
+             return Double.NaN;
+         } else if (isInfinite()) {
+             return isNegative() ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+         }
  
-   @Override
-   public void roundToMagnitude(int magnitude, MathContext mathContext) {
-     // The position in the BCD at which rounding will be performed; digits to the right of position
-     // will be rounded away.
-     // TODO: Andy: There was a test failure because of integer overflow here. Should I do
-     // "safe subtraction" everywhere in the code?  What's the nicest way to do it?
-     int position = safeSubtract(magnitude, scale);
-     // Enforce the number of digits required by the MathContext.
-     int _mcPrecision = mathContext.getPrecision();
-     if (magnitude == Integer.MAX_VALUE
-         || (_mcPrecision > 0 && precision - position > _mcPrecision)) {
-       position = precision - _mcPrecision;
-     }
-     if (position <= 0 && !isApproximate) {
-       // All digits are to the left of the rounding magnitude.
-     } else if (precision == 0) {
-       // No rounding for zero.
-     } else {
-       // Perform rounding logic.
-       // "leading" = most significant digit to the right of rounding
-       // "trailing" = least significant digit to the left of rounding
-       byte leadingDigit = getDigitPos(safeSubtract(position, 1));
-       byte trailingDigit = getDigitPos(position);
-       // Compute which section of the number we are in.
-       // EDGE means we are at the bottom or top edge, like 1.000 or 1.999 (used by doubles)
-       // LOWER means we are between the bottom edge and the midpoint, like 1.391
-       // MIDPOINT means we are exactly in the middle, like 1.500
-       // UPPER means we are between the midpoint and the top edge, like 1.916
-       int section = RoundingUtils.SECTION_MIDPOINT;
-       if (!isApproximate) {
-         if (leadingDigit < 5) {
-           section = RoundingUtils.SECTION_LOWER;
-         } else if (leadingDigit > 5) {
-           section = RoundingUtils.SECTION_UPPER;
-         } else {
-           for (int p = safeSubtract(position, 2); p >= 0; p--) {
-             if (getDigitPos(p) != 0) {
-               section = RoundingUtils.SECTION_UPPER;
-               break;
-             }
-           }
+         long tempLong = 0L;
+         int lostDigits = precision - Math.min(precision, 17);
+         for (int shift = precision - 1; shift >= lostDigits; shift--) {
+             tempLong = tempLong * 10 + getDigitPos(shift);
          }
-       } else {
-         int p = safeSubtract(position, 2);
-         int minP = Math.max(0, precision - 14);
-         if (leadingDigit == 0) {
-           section = SECTION_LOWER_EDGE;
-           for (; p >= minP; p--) {
-             if (getDigitPos(p) != 0) {
-               section = RoundingUtils.SECTION_LOWER;
-               break;
-             }
-           }
-         } else if (leadingDigit == 4) {
-           for (; p >= minP; p--) {
-             if (getDigitPos(p) != 9) {
-               section = RoundingUtils.SECTION_LOWER;
-               break;
-             }
-           }
-         } else if (leadingDigit == 5) {
-           for (; p >= minP; p--) {
-             if (getDigitPos(p) != 0) {
-               section = RoundingUtils.SECTION_UPPER;
-               break;
-             }
-           }
-         } else if (leadingDigit == 9) {
-           section = SECTION_UPPER_EDGE;
-           for (; p >= minP; p--) {
-             if (getDigitPos(p) != 9) {
-               section = RoundingUtils.SECTION_UPPER;
-               break;
-             }
-           }
-         } else if (leadingDigit < 5) {
-           section = RoundingUtils.SECTION_LOWER;
+         double result = tempLong;
+         int _scale = scale + lostDigits;
+         if (_scale >= 0) {
+             // 1e22 is the largest exact double.
+             int i = _scale;
+             for (; i >= 22; i -= 22)
+                 result *= 1e22;
+             result *= DOUBLE_MULTIPLIERS[i];
          } else {
-           section = RoundingUtils.SECTION_UPPER;
+             // 1e22 is the largest exact double.
+             int i = _scale;
+             for (; i <= -22; i += 22)
+                 result /= 1e22;
+             result /= DOUBLE_MULTIPLIERS[-i];
          }
+         if (isNegative())
+             result = -result;
+         return result;
+     }
  
-         boolean roundsAtMidpoint =
-             RoundingUtils.roundsAtMidpoint(mathContext.getRoundingMode().ordinal());
-         if (safeSubtract(position, 1) < precision - 14
-             || (roundsAtMidpoint && section == RoundingUtils.SECTION_MIDPOINT)
-             || (!roundsAtMidpoint && section < 0 /* i.e. at upper or lower edge */)) {
-           // Oops! This means that we have to get the exact representation of the double, because
-           // the zone of uncertainty is along the rounding boundary.
-           convertToAccurateDouble();
-           roundToMagnitude(magnitude, mathContext); // start over
-           return;
+     @Override
+     public BigDecimal toBigDecimal() {
+         if (isApproximate) {
+             // Converting to a BigDecimal requires Double.toString().
+             convertToAccurateDouble();
          }
+         return bcdToBigDecimal();
+     }
  
-         // Turn off the approximate double flag, since the value is now confirmed to be exact.
-         isApproximate = false;
-         origDouble = 0.0;
-         origDelta = 0;
-         if (position <= 0) {
-           // All digits are to the left of the rounding magnitude.
-           return;
+     protected double toDoubleFromOriginal() {
+         double result = origDouble;
+         int delta = origDelta;
+         if (delta >= 0) {
+             // 1e22 is the largest exact double.
+             for (; delta >= 22; delta -= 22)
+                 result *= 1e22;
+             result *= DOUBLE_MULTIPLIERS[delta];
+         } else {
+             // 1e22 is the largest exact double.
+             for (; delta <= -22; delta += 22)
+                 result /= 1e22;
+             result /= DOUBLE_MULTIPLIERS[-delta];
          }
+         if (isNegative())
+             result *= -1;
+         return result;
+     }
  
-         // Good to continue rounding.
-         if (section == SECTION_LOWER_EDGE) section = RoundingUtils.SECTION_LOWER;
-         if (section == SECTION_UPPER_EDGE) section = RoundingUtils.SECTION_UPPER;
-       }
+     private static int safeSubtract(int a, int b) {
+         int diff = a - b;
+         if (b < 0 && diff < a)
+             return Integer.MAX_VALUE;
+         if (b > 0 && diff > a)
+             return Integer.MIN_VALUE;
+         return diff;
+     }
  
-       boolean roundDown =
-           RoundingUtils.getRoundingDirection(
-               (trailingDigit % 2) == 0,
-               isNegative(),
-               section,
-               mathContext.getRoundingMode().ordinal(),
-               this);
+     private static final int SECTION_LOWER_EDGE = -1;
+     private static final int SECTION_UPPER_EDGE = -2;
+     @Override
+     public void roundToMagnitude(int magnitude, MathContext mathContext) {
+         // The position in the BCD at which rounding will be performed; digits to the right of position
+         // will be rounded away.
+         // TODO: Andy: There was a test failure because of integer overflow here. Should I do
+         // "safe subtraction" everywhere in the code? What's the nicest way to do it?
+         int position = safeSubtract(magnitude, scale);
+         // Enforce the number of digits required by the MathContext.
+         int _mcPrecision = mathContext.getPrecision();
+         if (magnitude == Integer.MAX_VALUE
+                 || (_mcPrecision > 0 && precision - position > _mcPrecision)) {
+             position = precision - _mcPrecision;
+         }
  
-       // Perform truncation
-       if (position >= precision) {
-         setBcdToZero();
-         scale = magnitude;
-       } else {
-         shiftRight(position);
-       }
+         if (position <= 0 && !isApproximate) {
+             // All digits are to the left of the rounding magnitude.
+         } else if (precision == 0) {
+             // No rounding for zero.
+         } else {
+             // Perform rounding logic.
+             // "leading" = most significant digit to the right of rounding
+             // "trailing" = least significant digit to the left of rounding
+             byte leadingDigit = getDigitPos(safeSubtract(position, 1));
+             byte trailingDigit = getDigitPos(position);
+             // Compute which section of the number we are in.
+             // EDGE means we are at the bottom or top edge, like 1.000 or 1.999 (used by doubles)
+             // LOWER means we are between the bottom edge and the midpoint, like 1.391
+             // MIDPOINT means we are exactly in the middle, like 1.500
+             // UPPER means we are between the midpoint and the top edge, like 1.916
+             int section = RoundingUtils.SECTION_MIDPOINT;
+             if (!isApproximate) {
+                 if (leadingDigit < 5) {
+                     section = RoundingUtils.SECTION_LOWER;
+                 } else if (leadingDigit > 5) {
+                     section = RoundingUtils.SECTION_UPPER;
+                 } else {
+                     for (int p = safeSubtract(position, 2); p >= 0; p--) {
+                         if (getDigitPos(p) != 0) {
+                             section = RoundingUtils.SECTION_UPPER;
+                             break;
+                         }
+                     }
+                 }
+             } else {
+                 int p = safeSubtract(position, 2);
+                 int minP = Math.max(0, precision - 14);
+                 if (leadingDigit == 0) {
+                     section = SECTION_LOWER_EDGE;
+                     for (; p >= minP; p--) {
+                         if (getDigitPos(p) != 0) {
+                             section = RoundingUtils.SECTION_LOWER;
+                             break;
+                         }
+                     }
+                 } else if (leadingDigit == 4) {
+                     for (; p >= minP; p--) {
+                         if (getDigitPos(p) != 9) {
+                             section = RoundingUtils.SECTION_LOWER;
+                             break;
+                         }
+                     }
+                 } else if (leadingDigit == 5) {
+                     for (; p >= minP; p--) {
+                         if (getDigitPos(p) != 0) {
+                             section = RoundingUtils.SECTION_UPPER;
+                             break;
+                         }
+                     }
+                 } else if (leadingDigit == 9) {
+                     section = SECTION_UPPER_EDGE;
+                     for (; p >= minP; p--) {
+                         if (getDigitPos(p) != 9) {
+                             section = RoundingUtils.SECTION_UPPER;
+                             break;
+                         }
+                     }
+                 } else if (leadingDigit < 5) {
+                     section = RoundingUtils.SECTION_LOWER;
+                 } else {
+                     section = RoundingUtils.SECTION_UPPER;
+                 }
+                 boolean roundsAtMidpoint = RoundingUtils
+                         .roundsAtMidpoint(mathContext.getRoundingMode().ordinal());
+                 if (safeSubtract(position, 1) < precision - 14
+                         || (roundsAtMidpoint && section == RoundingUtils.SECTION_MIDPOINT)
+                         || (!roundsAtMidpoint && section < 0 /* i.e. at upper or lower edge */)) {
+                     // Oops! This means that we have to get the exact representation of the double,
+                     // because
+                     // the zone of uncertainty is along the rounding boundary.
+                     convertToAccurateDouble();
+                     roundToMagnitude(magnitude, mathContext); // start over
+                     return;
+                 }
+                 // Turn off the approximate double flag, since the value is now confirmed to be exact.
+                 isApproximate = false;
+                 origDouble = 0.0;
+                 origDelta = 0;
+                 if (position <= 0) {
+                     // All digits are to the left of the rounding magnitude.
+                     return;
+                 }
+                 // Good to continue rounding.
+                 if (section == SECTION_LOWER_EDGE)
+                     section = RoundingUtils.SECTION_LOWER;
+                 if (section == SECTION_UPPER_EDGE)
+                     section = RoundingUtils.SECTION_UPPER;
+             }
  
-       // Bubble the result to the higher digits
-       if (!roundDown) {
-         if (trailingDigit == 9) {
-           int bubblePos = 0;
-           // Note: in the long implementation, the most digits BCD can have at this point is 15,
-           // so bubblePos <= 15 and getDigitPos(bubblePos) is safe.
-           for (; getDigitPos(bubblePos) == 9; bubblePos++) {}
-           shiftRight(bubblePos); // shift off the trailing 9s
-         }
-         byte digit0 = getDigitPos(0);
-         assert digit0 != 9;
-         setDigitPos(0, (byte) (digit0 + 1));
-         precision += 1; // in case an extra digit got added
-       }
+             boolean roundDown = RoundingUtils.getRoundingDirection((trailingDigit % 2) == 0,
+                     isNegative(),
+                     section,
+                     mathContext.getRoundingMode().ordinal(),
+                     this);
+             // Perform truncation
+             if (position >= precision) {
+                 setBcdToZero();
+                 scale = magnitude;
+             } else {
+                 shiftRight(position);
+             }
+             // Bubble the result to the higher digits
+             if (!roundDown) {
+                 if (trailingDigit == 9) {
+                     int bubblePos = 0;
+                     // Note: in the long implementation, the most digits BCD can have at this point is
+                     // 15,
+                     // so bubblePos <= 15 and getDigitPos(bubblePos) is safe.
+                     for (; getDigitPos(bubblePos) == 9; bubblePos++) {
+                     }
+                     shiftRight(bubblePos); // shift off the trailing 9s
+                 }
+                 byte digit0 = getDigitPos(0);
+                 assert digit0 != 9;
+                 setDigitPos(0, (byte) (digit0 + 1));
+                 precision += 1; // in case an extra digit got added
+             }
  
-       compact();
+             compact();
+         }
      }
-   }
  
-   @Override
-   public void roundToInfinity() {
-     if (isApproximate) {
-       convertToAccurateDouble();
+     @Override
+     public void roundToInfinity() {
+         if (isApproximate) {
+             convertToAccurateDouble();
+         }
      }
-   }
  
++<<<<<<< .working
 +  @Override
 +  public void truncate() {
 +      if (scale < 0) {
 +          shiftRight(-scale);
 +          scale = 0;
 +          compact();
 +      }
 +  }
 +
 +  /**
 +   * Appends a digit, optionally with one or more leading zeros, to the end of the value represented
 +   * by this DecimalQuantity.
 +   *
 +   * <p>The primary use of this method is to construct numbers during a parsing loop. It allows
 +   * parsing to take advantage of the digit list infrastructure primarily designed for formatting.
 +   *
 +   * @param value The digit to append.
 +   * @param leadingZeros The number of zeros to append before the digit. For example, if the value
 +   *     in this instance starts as 12.3, and you append a 4 with 1 leading zero, the value becomes
 +   *     12.304.
 +   * @param appendAsInteger If true, increase the magnitude of existing digits to make room for the
 +   *     new digit. If false, append to the end like a fraction digit. If true, there must not be
 +   *     any fraction digits already in the number.
 +   * @internal
 +   * @deprecated This API is ICU internal only.
 +   */
 +  @Deprecated
 +  public void appendDigit(byte value, int leadingZeros, boolean appendAsInteger) {
 +    assert leadingZeros >= 0;
++=======
+     /**
+      * Appends a digit, optionally with one or more leading zeros, to the end of the value represented by
+      * this DecimalQuantity.
+      *
+      * <p>
+      * The primary use of this method is to construct numbers during a parsing loop. It allows parsing to
+      * take advantage of the digit list infrastructure primarily designed for formatting.
+      *
+      * @param value
+      *            The digit to append.
+      * @param leadingZeros
+      *            The number of zeros to append before the digit. For example, if the value in this
+      *            instance starts as 12.3, and you append a 4 with 1 leading zero, the value becomes
+      *            12.304.
+      * @param appendAsInteger
+      *            If true, increase the magnitude of existing digits to make room for the new digit. If
+      *            false, append to the end like a fraction digit. If true, there must not be any fraction
+      *            digits already in the number.
+      * @internal
+      * @deprecated This API is ICU internal only.
+      */
+     @Deprecated
+     public void appendDigit(byte value, int leadingZeros, boolean appendAsInteger) {
+         assert leadingZeros >= 0;
++>>>>>>> .merge-right.r40750
+         // Zero requires special handling to maintain the invariant that the least-significant digit
+         // in the BCD is nonzero.
+         if (value == 0) {
+             if (appendAsInteger && precision != 0) {
+                 scale += leadingZeros + 1;
+             }
+             return;
+         }
  
-     // Zero requires special handling to maintain the invariant that the least-significant digit
-     // in the BCD is nonzero.
-     if (value == 0) {
-       if (appendAsInteger && precision != 0) {
-         scale += leadingZeros + 1;
-       }
-       return;
-     }
-     // Deal with trailing zeros
-     if (scale > 0) {
-       leadingZeros += scale;
-       if (appendAsInteger) {
-         scale = 0;
-       }
-     }
+         // Deal with trailing zeros
+         if (scale > 0) {
+             leadingZeros += scale;
+             if (appendAsInteger) {
+                 scale = 0;
+             }
+         }
  
-     // Append digit
-     shiftLeft(leadingZeros + 1);
-     setDigitPos(0, value);
+         // Append digit
+         shiftLeft(leadingZeros + 1);
+         setDigitPos(0, value);
  
-     // Fix scale if in integer mode
-     if (appendAsInteger) {
-       scale += leadingZeros + 1;
+         // Fix scale if in integer mode
+         if (appendAsInteger) {
+             scale += leadingZeros + 1;
+         }
      }
-   }
-   @Override
-   public String toPlainString() {
-       // NOTE: This logic is duplicated between here and DecimalQuantity_SimpleStorage.
-       StringBuilder sb = new StringBuilder();
-       if (isNegative()) {
-           sb.append('-');
-       }
-       for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) {
-         sb.append(getDigit(m));
-         if (m == 0) sb.append('.');
-       }
-       return sb.toString();
-   }
-   /**
-    * Returns a single digit from the BCD list. No internal state is changed by calling this method.
-    *
-    * @param position The position of the digit to pop, counted in BCD units from the least
-    *     significant digit. If outside the range supported by the implementation, zero is returned.
-    * @return The digit at the specified location.
-    */
-   protected abstract byte getDigitPos(int position);
-   /**
-    * Sets the digit in the BCD list. This method only sets the digit; it is the caller's
-    * responsibility to call {@link #compact} after setting the digit.
-    *
-    * @param position The position of the digit to pop, counted in BCD units from the least
-    *     significant digit. If outside the range supported by the implementation, an AssertionError
-    *     is thrown.
-    * @param value The digit to set at the specified location.
-    */
-   protected abstract void setDigitPos(int position, byte value);
  
-   /**
-    * Adds zeros to the end of the BCD list. This will result in an invalid BCD representation; it is
-    * the caller's responsibility to do further manipulation and then call {@link #compact}.
-    *
-    * @param numDigits The number of zeros to add.
-    */
-   protected abstract void shiftLeft(int numDigits);
+     @Override
+     public String toPlainString() {
+         // NOTE: This logic is duplicated between here and DecimalQuantity_SimpleStorage.
+         StringBuilder sb = new StringBuilder();
+         if (isNegative()) {
+             sb.append('-');
+         }
+         for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) {
+             sb.append(getDigit(m));
+             if (m == 0)
+                 sb.append('.');
+         }
+         return sb.toString();
+     }
  
-   /**
-    * Sets the internal representation to zero. Clears any values stored in scale, precision,
-    * hasDouble, origDouble, origDelta, and BCD data.
-    */
-   protected abstract void setBcdToZero();
-   /**
-    * Sets the internal BCD state to represent the value in the given int. The int is guaranteed to
-    * be either positive. The internal state is guaranteed to be empty when this method is called.
-    *
-    * @param n The value to consume.
-    */
-   protected abstract void readIntToBcd(int input);
-   /**
-    * Sets the internal BCD state to represent the value in the given long. The long is guaranteed to
-    * be either positive. The internal state is guaranteed to be empty when this method is called.
-    *
-    * @param n The value to consume.
-    */
-   protected abstract void readLongToBcd(long input);
-   /**
-    * Sets the internal BCD state to represent the value in the given BigInteger. The BigInteger is
-    * guaranteed to be positive, and it is guaranteed to be larger than Long.MAX_VALUE. The internal
-    * state is guaranteed to be empty when this method is called.
-    *
-    * @param n The value to consume.
-    */
-   protected abstract void readBigIntegerToBcd(BigInteger input);
-   /**
-    * Returns a BigDecimal encoding the internal BCD value.
-    *
-    * @return A BigDecimal representation of the internal BCD.
-    */
-   protected abstract BigDecimal bcdToBigDecimal();
-   protected abstract void copyBcdFrom(DecimalQuantity _other);
-   /**
-    * Removes trailing zeros from the BCD (adjusting the scale as required) and then computes the
-    * precision. The precision is the number of digits in the number up through the greatest nonzero
-    * digit.
-    *
-    * <p>This method must always be called when bcd changes in order for assumptions to be correct in
-    * methods like {@link #fractionCount()}.
-    */
-   protected abstract void compact();
+     /**
+      * Returns a single digit from the BCD list. No internal state is changed by calling this method.
+      *
+      * @param position
+      *            The position of the digit to pop, counted in BCD units from the least significant
+      *            digit. If outside the range supported by the implementation, zero is returned.
+      * @return The digit at the specified location.
+      */
+     protected abstract byte getDigitPos(int position);
+     /**
+      * Sets the digit in the BCD list. This method only sets the digit; it is the caller's responsibility
+      * to call {@link #compact} after setting the digit.
+      *
+      * @param position
+      *            The position of the digit to pop, counted in BCD units from the least significant
+      *            digit. If outside the range supported by the implementation, an AssertionError is
+      *            thrown.
+      * @param value
+      *            The digit to set at the specified location.
+      */
+     protected abstract void setDigitPos(int position, byte value);
+     /**
+      * Adds zeros to the end of the BCD list. This will result in an invalid BCD representation; it is
+      * the caller's responsibility to do further manipulation and then call {@link #compact}.
+      *
+      * @param numDigits
+      *            The number of zeros to add.
+      */
+     protected abstract void shiftLeft(int numDigits);
++<<<<<<< .working
 +  /**
 +   * Removes digits from the end of the BCD list. This may result in an invalid BCD representation; it is
 +   * the caller's responsibility to follow-up with a call to {@link #compact}.
 +   *
 +   * @param numDigits The number of zeros to add.
 +   */
 +  protected abstract void shiftRight(int numDigits);
++=======
+     protected abstract void shiftRight(int numDigits);
++>>>>>>> .merge-right.r40750
+     /**
+      * Sets the internal representation to zero. Clears any values stored in scale, precision, hasDouble,
+      * origDouble, origDelta, and BCD data.
+      */
+     protected abstract void setBcdToZero();
+     /**
+      * Sets the internal BCD state to represent the value in the given int. The int is guaranteed to be
+      * either positive. The internal state is guaranteed to be empty when this method is called.
+      *
+      * @param n
+      *            The value to consume.
+      */
+     protected abstract void readIntToBcd(int input);
+     /**
+      * Sets the internal BCD state to represent the value in the given long. The long is guaranteed to be
+      * either positive. The internal state is guaranteed to be empty when this method is called.
+      *
+      * @param n
+      *            The value to consume.
+      */
+     protected abstract void readLongToBcd(long input);
+     /**
+      * Sets the internal BCD state to represent the value in the given BigInteger. The BigInteger is
+      * guaranteed to be positive, and it is guaranteed to be larger than Long.MAX_VALUE. The internal
+      * state is guaranteed to be empty when this method is called.
+      *
+      * @param n
+      *            The value to consume.
+      */
+     protected abstract void readBigIntegerToBcd(BigInteger input);
+     /**
+      * Returns a BigDecimal encoding the internal BCD value.
+      *
+      * @return A BigDecimal representation of the internal BCD.
+      */
+     protected abstract BigDecimal bcdToBigDecimal();
+     protected abstract void copyBcdFrom(DecimalQuantity _other);
+     /**
+      * Removes trailing zeros from the BCD (adjusting the scale as required) and then computes the
+      * precision. The precision is the number of digits in the number up through the greatest nonzero
+      * digit.
+      *
+      * <p>
+      * This method must always be called when bcd changes in order for assumptions to be correct in
+      * methods like {@link #fractionCount()}.
+      */
+     protected abstract void compact();
  }
index 929c50b06017be15f3134883a85935e50966fbdf,9f8ea1fcd27709ab9c1492f9a5daa4b7990aab70..3eb1a9b3b6e338fad83eaba8d456356fc3f45df3
@@@ -101,11 -103,15 +103,15 @@@ public class MutablePatternModifie
       * @param unitWidth
       *            The width used to render currencies.
       * @param rules
-      *            Required if the triple currency sign, "¤¤¤", appears in the pattern, which can be determined from the
-      *            convenience method {@link #needsPlurals()}.
+      *            Required if the triple currency sign, "¤¤¤", appears in the pattern, which can be
+      *            determined from the convenience method {@link #needsPlurals()}.
       */
-     public void setSymbols(DecimalFormatSymbols symbols, Currency currency, UnitWidth unitWidth, PluralRules rules) {
+     public void setSymbols(
+             DecimalFormatSymbols symbols,
+             Currency currency,
+             UnitWidth unitWidth,
+             PluralRules rules) {
 -        assert (rules != null) == needsPlurals();
 +        //assert (rules != null) == needsPlurals();
          this.symbols = symbols;
          this.currency = currency;
          this.unitWidth = unitWidth;
index c07d88b7705e97917fd49936f66cdbc6030fb864,49f1f1e0aafe08448103d4c62ede79965bc6c48d..4c082fc3debeecbf22eb3f79d447cbd5081ea23d
@@@ -58,10 -62,12 +60,12 @@@ final class NumberPropertyMapper 
       * @param symbols
       *            The symbols associated with the property bag.
       * @param exportedProperties
 -     *            A property bag in which to store validated properties.
 +     *            A property bag in which to store validated properties. Used by some DecimalFormat getters.
       * @return A new MacroProps containing all of the information in the Properties.
       */
-     public static MacroProps oldToNew(DecimalFormatProperties properties, DecimalFormatSymbols symbols,
+     public static MacroProps oldToNew(
+             DecimalFormatProperties properties,
+             DecimalFormatSymbols symbols,
              DecimalFormatProperties exportedProperties) {
          MacroProps macros = new MacroProps();
          ULocale locale = symbols.getULocale();
index f10fad7d5dfffd99fbf53c650bb13a48c53a64f5,b29d3482f3d3484f76d50f961aa07358f032367e..b7f3e9948675ca9bc52953769720b39f4f9ce314
@@@ -21,229 -19,199 +20,221 @@@ public class AffixUtilsTest 
              // Use interesting symbols where possible. The symbols are from ar_SA but are hard-coded
              // here to make the test independent of locale data changes.
              switch (type) {
-               case AffixUtils.TYPE_MINUS_SIGN:
+             case AffixUtils.TYPE_MINUS_SIGN:
                  return "−";
-               case AffixUtils.TYPE_PLUS_SIGN:
+             case AffixUtils.TYPE_PLUS_SIGN:
                  return "\u061C+";
-               case AffixUtils.TYPE_PERCENT:
+             case AffixUtils.TYPE_PERCENT:
                  return "٪\u061C";
-               case AffixUtils.TYPE_PERMILLE:
+             case AffixUtils.TYPE_PERMILLE:
                  return "؉";
-               case AffixUtils.TYPE_CURRENCY_SINGLE:
+             case AffixUtils.TYPE_CURRENCY_SINGLE:
                  return "$";
-               case AffixUtils.TYPE_CURRENCY_DOUBLE:
+             case AffixUtils.TYPE_CURRENCY_DOUBLE:
                  return "XXX";
-               case AffixUtils.TYPE_CURRENCY_TRIPLE:
+             case AffixUtils.TYPE_CURRENCY_TRIPLE:
                  return "long name";
-               case AffixUtils.TYPE_CURRENCY_QUAD:
+             case AffixUtils.TYPE_CURRENCY_QUAD:
                  return "\uFFFD";
-               case AffixUtils.TYPE_CURRENCY_QUINT:
+             case AffixUtils.TYPE_CURRENCY_QUINT:
                  return "@";
-               case AffixUtils.TYPE_CURRENCY_OVERFLOW:
+             case AffixUtils.TYPE_CURRENCY_OVERFLOW:
                  return "\uFFFD";
-               default:
+             default:
                  throw new AssertionError();
              }
-           }
-         };
-   @Test
-   public void testEscape() {
-     Object[][] cases = {
-       {"", ""},
-       {"abc", "abc"},
-       {"-", "'-'"},
-       {"-!", "'-'!"},
-       {"−", "−"},
-       {"---", "'---'"},
-       {"-%-", "'-%-'"},
-       {"'", "''"},
-       {"-'", "'-'''"},
-       {"-'-", "'-''-'"},
-       {"a-'-", "a'-''-'"}
+         }
      };
  
-     StringBuilder sb = new StringBuilder();
-     for (Object[] cas : cases) {
-       String input = (String) cas[0];
-       String expected = (String) cas[1];
-       sb.setLength(0);
-       AffixUtils.escape(input, sb);
-       assertEquals(expected, sb.toString());
+     @Test
+     public void testEscape() {
+         Object[][] cases = {
+                 { "", "" },
+                 { "abc", "abc" },
+                 { "-", "'-'" },
+                 { "-!", "'-'!" },
+                 { "−", "−" },
+                 { "---", "'---'" },
+                 { "-%-", "'-%-'" },
+                 { "'", "''" },
+                 { "-'", "'-'''" },
+                 { "-'-", "'-''-'" },
+                 { "a-'-", "a'-''-'" } };
+         StringBuilder sb = new StringBuilder();
+         for (Object[] cas : cases) {
+             String input = (String) cas[0];
+             String expected = (String) cas[1];
+             sb.setLength(0);
+             AffixUtils.escape(input, sb);
+             assertEquals(expected, sb.toString());
+         }
      }
-   }
-   @Test
-   public void testUnescape() {
-     Object[][] cases = {
-       {"", false, 0, ""},
-       {"abc", false, 3, "abc"},
-       {"-", false, 1, "−"},
-       {"-!", false, 2, "−!"},
-       {"+", false, 1, "\u061C+"},
-       {"+!", false, 2, "\u061C+!"},
-       {"‰", false, 1, "؉"},
-       {"‰!", false, 2, "؉!"},
-       {"-x", false, 2, "−x"},
-       {"'-'x", false, 2, "-x"},
-       {"'--''-'-x", false, 6, "--'-−x"},
-       {"''", false, 1, "'"},
-       {"''''", false, 2, "''"},
-       {"''''''", false, 3, "'''"},
-       {"''x''", false, 3, "'x'"},
-       {"¤", true, 1, "$"},
-       {"¤¤", true, 2, "XXX"},
-       {"¤¤¤", true, 3, "long name"},
-       {"¤¤¤¤", true, 4, "\uFFFD"},
-       {"¤¤¤¤¤", true, 5, "@"},
-       {"¤¤¤¤¤¤", true, 6, "\uFFFD"},
-       {"¤¤¤a¤¤¤¤", true, 8, "long namea\uFFFD"},
-       {"a¤¤¤¤b¤¤¤¤¤c", true, 12, "a\uFFFDb@c"},
-       {"¤!", true, 2, "$!"},
-       {"¤¤!", true, 3, "XXX!"},
-       {"¤¤¤!", true, 4, "long name!"},
-       {"-¤¤", true, 3, "−XXX"},
-       {"¤¤-", true, 3, "XXX−"},
-       {"'¤'", false, 1, "¤"},
-       {"%", false, 1, "٪\u061C"},
-       {"'%'", false, 1, "%"},
-       {"¤'-'%", true, 3, "$-٪\u061C"},
-       {"#0#@#*#;#", false, 9, "#0#@#*#;#"}
-     };
  
-     for (Object[] cas : cases) {
-       String input = (String) cas[0];
-       boolean curr = (Boolean) cas[1];
-       int length = (Integer) cas[2];
-       String output = (String) cas[3];
-       assertEquals(
-           "Currency on <" + input + ">", curr, AffixUtils.hasCurrencySymbols(input));
-       assertEquals("Length on <" + input + ">", length, AffixUtils.estimateLength(input));
-       String actual = unescapeWithDefaults(input);
-       assertEquals("Output on <" + input + ">", output, actual);
-       int ulength = AffixUtils.unescapedCodePointCount(input, DEFAULT_SYMBOL_PROVIDER);
-       assertEquals("Unescaped length on <" + input + ">", output.length(), ulength);
+     @Test
+     public void testUnescape() {
+         Object[][] cases = {
+                 { "", false, 0, "" },
+                 { "abc", false, 3, "abc" },
+                 { "-", false, 1, "−" },
+                 { "-!", false, 2, "−!" },
+                 { "+", false, 1, "\u061C+" },
+                 { "+!", false, 2, "\u061C+!" },
+                 { "‰", false, 1, "؉" },
+                 { "‰!", false, 2, "؉!" },
+                 { "-x", false, 2, "−x" },
+                 { "'-'x", false, 2, "-x" },
+                 { "'--''-'-x", false, 6, "--'-−x" },
+                 { "''", false, 1, "'" },
+                 { "''''", false, 2, "''" },
+                 { "''''''", false, 3, "'''" },
+                 { "''x''", false, 3, "'x'" },
+                 { "¤", true, 1, "$" },
+                 { "¤¤", true, 2, "XXX" },
+                 { "¤¤¤", true, 3, "long name" },
+                 { "¤¤¤¤", true, 4, "\uFFFD" },
+                 { "¤¤¤¤¤", true, 5, "@" },
+                 { "¤¤¤¤¤¤", true, 6, "\uFFFD" },
+                 { "¤¤¤a¤¤¤¤", true, 8, "long namea\uFFFD" },
+                 { "a¤¤¤¤b¤¤¤¤¤c", true, 12, "a\uFFFDb@c" },
+                 { "¤!", true, 2, "$!" },
+                 { "¤¤!", true, 3, "XXX!" },
+                 { "¤¤¤!", true, 4, "long name!" },
+                 { "-¤¤", true, 3, "−XXX" },
+                 { "¤¤-", true, 3, "XXX−" },
+                 { "'¤'", false, 1, "¤" },
+                 { "%", false, 1, "٪\u061C" },
+                 { "'%'", false, 1, "%" },
+                 { "¤'-'%", true, 3, "$-٪\u061C" },
+                 { "#0#@#*#;#", false, 9, "#0#@#*#;#" } };
+         for (Object[] cas : cases) {
+             String input = (String) cas[0];
+             boolean curr = (Boolean) cas[1];
+             int length = (Integer) cas[2];
+             String output = (String) cas[3];
+             assertEquals("Currency on <" + input + ">", curr, AffixUtils.hasCurrencySymbols(input));
+             assertEquals("Length on <" + input + ">", length, AffixUtils.estimateLength(input));
+             String actual = unescapeWithDefaults(input);
+             assertEquals("Output on <" + input + ">", output, actual);
+             int ulength = AffixUtils.unescapedCodePointCount(input, DEFAULT_SYMBOL_PROVIDER);
+             assertEquals("Unescaped length on <" + input + ">", output.length(), ulength);
+         }
      }
-   }
  
-   @Test
-   public void testContainsReplaceType() {
-     Object[][] cases = {
-       {"", false, ""},
-       {"-", true, "+"},
-       {"-a", true, "+a"},
-       {"a-", true, "a+"},
-       {"a-b", true, "a+b"},
-       {"--", true, "++"},
-       {"x", false, "x"}
-     };
-     for (Object[] cas : cases) {
-       String input = (String) cas[0];
-       boolean hasMinusSign = (Boolean) cas[1];
-       String output = (String) cas[2];
-       assertEquals(
-           "Contains on input " + input,
-           hasMinusSign,
-           AffixUtils.containsType(input, AffixUtils.TYPE_MINUS_SIGN));
-       assertEquals(
-           "Replace on input" + input,
-           output,
-           AffixUtils.replaceType(input, AffixUtils.TYPE_MINUS_SIGN, '+'));
+     @Test
+     public void testContainsReplaceType() {
+         Object[][] cases = {
+                 { "", false, "" },
+                 { "-", true, "+" },
+                 { "-a", true, "+a" },
+                 { "a-", true, "a+" },
+                 { "a-b", true, "a+b" },
+                 { "--", true, "++" },
+                 { "x", false, "x" } };
+         for (Object[] cas : cases) {
+             String input = (String) cas[0];
+             boolean hasMinusSign = (Boolean) cas[1];
+             String output = (String) cas[2];
+             assertEquals("Contains on input " + input,
+                     hasMinusSign,
+                     AffixUtils.containsType(input, AffixUtils.TYPE_MINUS_SIGN));
+             assertEquals("Replace on input" + input,
+                     output,
+                     AffixUtils.replaceType(input, AffixUtils.TYPE_MINUS_SIGN, '+'));
+         }
      }
-   }
  
-   @Test
-   public void testInvalid() {
-     String[] invalidExamples = {"'", "x'", "'x", "'x''", "''x'"};
-     for (String str : invalidExamples) {
-       try {
-         AffixUtils.hasCurrencySymbols(str);
-         fail("No exception was thrown on an invalid string");
-       } catch (IllegalArgumentException e) {
-         // OK
-       }
-       try {
-         AffixUtils.estimateLength(str);
-         fail("No exception was thrown on an invalid string");
-       } catch (IllegalArgumentException e) {
-         // OK
-       }
-       try {
-         unescapeWithDefaults(str);
-         fail("No exception was thrown on an invalid string");
-       } catch (IllegalArgumentException e) {
-         // OK
-       }
-     }
-   }
+     @Test
+     public void testInvalid() {
+         String[] invalidExamples = { "'", "x'", "'x", "'x''", "''x'" };
  
-   @Test
-   public void testUnescapeWithSymbolProvider() {
-     String[][] cases = {
-       {"", ""},
-       {"-", "1"},
-       {"'-'", "-"},
-       {"- + % ‰ ¤ ¤¤ ¤¤¤ ¤¤¤¤ ¤¤¤¤¤", "1 2 3 4 5 6 7 8 9"},
-       {"'¤¤¤¤¤¤'", "¤¤¤¤¤¤"},
-       {"¤¤¤¤¤¤", "\uFFFD"}
-     };
+         for (String str : invalidExamples) {
+             try {
+                 AffixUtils.hasCurrencySymbols(str);
+                 fail("No exception was thrown on an invalid string");
+             } catch (IllegalArgumentException e) {
+                 // OK
+             }
+             try {
+                 AffixUtils.estimateLength(str);
+                 fail("No exception was thrown on an invalid string");
+             } catch (IllegalArgumentException e) {
+                 // OK
+             }
+             try {
+                 unescapeWithDefaults(str);
+                 fail("No exception was thrown on an invalid string");
+             } catch (IllegalArgumentException e) {
+                 // OK
+             }
+         }
+     }
  
-     SymbolProvider provider =
-         new SymbolProvider() {
-           @Override
-           public CharSequence getSymbol(int type) {
-             return Integer.toString(Math.abs(type));
-           }
+     @Test
+     public void testUnescapeWithSymbolProvider() {
+         String[][] cases = {
+                 { "", "" },
+                 { "-", "1" },
+                 { "'-'", "-" },
+                 { "- + % ‰ ¤ ¤¤ ¤¤¤ ¤¤¤¤ ¤¤¤¤¤", "1 2 3 4 5 6 7 8 9" },
+                 { "'¤¤¤¤¤¤'", "¤¤¤¤¤¤" },
+                 { "¤¤¤¤¤¤", "\uFFFD" } };
+         SymbolProvider provider = new SymbolProvider() {
+             @Override
+             public CharSequence getSymbol(int type) {
+                 return Integer.toString(Math.abs(type));
+             }
          };
  
-     NumberStringBuilder sb = new NumberStringBuilder();
-     for (String[] cas : cases) {
-       String input = cas[0];
-       String expected = cas[1];
-       sb.clear();
-       AffixUtils.unescape(input, sb, 0, provider);
-       assertEquals("With symbol provider on <" + input + ">", expected, sb.toString());
+         NumberStringBuilder sb = new NumberStringBuilder();
+         for (String[] cas : cases) {
+             String input = cas[0];
+             String expected = cas[1];
+             sb.clear();
+             AffixUtils.unescape(input, sb, 0, provider);
+             assertEquals("With symbol provider on <" + input + ">", expected, sb.toString());
+         }
+         // Test insertion position
+         sb.clear();
+         sb.append("abcdefg", null);
+         AffixUtils.unescape("-+%", sb, 4, provider);
+         assertEquals("Symbol provider into middle", "abcd123efg", sb.toString());
      }
  
-     // Test insertion position
-     sb.clear();
-     sb.append("abcdefg", null);
-     AffixUtils.unescape("-+%", sb, 4, provider);
-     assertEquals("Symbol provider into middle", "abcd123efg", sb.toString());
-   }
 +  @Test
 +  public void testWithoutSymbolsOrIgnorables() {
 +    String[][] cases = {
 +        {"", ""},
 +        {"-", ""},
 +        {" ", ""},
 +        {"'-'", "-"},
 +        {" a + b ", "a  b"},
 +        {"-a+b%c‰d¤e¤¤f¤¤¤g¤¤¤¤h¤¤¤¤¤i", "abcdefghi"},
 +    };
 +
 +    UnicodeSet ignorables = new UnicodeSet("[:whitespace:]");
 +    StringBuilder sb = new StringBuilder();
 +    for (String[] cas : cases) {
 +      String input = cas[0];
 +      String expected = cas[1];
 +      sb.setLength(0);
 +      AffixUtils.trimSymbolsAndIgnorables(input, ignorables, sb);
 +      assertEquals("Removing symbols from: " + input, expected, sb.toString());
 +    }
 +  }
 +
-   private static String unescapeWithDefaults(String input) {
-     NumberStringBuilder nsb = new NumberStringBuilder();
-     int length = AffixUtils.unescape(input, nsb, 0, DEFAULT_SYMBOL_PROVIDER);
-     assertEquals("Return value of unescape", nsb.length(), length);
-     return nsb.toString();
-   }
+     private static String unescapeWithDefaults(String input) {
+         NumberStringBuilder nsb = new NumberStringBuilder();
+         int length = AffixUtils.unescape(input, nsb, 0, DEFAULT_SYMBOL_PROVIDER);
+         assertEquals("Return value of unescape", nsb.length(), length);
+         return nsb.toString();
+     }
  }
index dca7d30074d848d0249422b95fae67e50d613b64,d8ce44129bb23c019776351a7cc8151281824682..28c7f61a00750656844218c31e4114a0a93af05b
@@@ -32,534 -31,487 +32,519 @@@ import com.ibm.icu.util.ULocale
  @RunWith(JUnit4.class)
  public class DecimalQuantityTest extends TestFmwk {
  
-   @Ignore
-   @Test
-   public void testBehavior() throws ParseException {
-     // Make a list of several formatters to test the behavior of DecimalQuantity.
-     List<LocalizedNumberFormatter> formats = new ArrayList<LocalizedNumberFormatter>();
-     DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(ULocale.ENGLISH);
-     DecimalFormatProperties properties = new DecimalFormatProperties();
-     formats.add(NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH));
-     properties =
-         new DecimalFormatProperties()
-             .setMinimumSignificantDigits(3)
-             .setMaximumSignificantDigits(3)
-             .setCompactStyle(CompactStyle.LONG);
-     formats.add(NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH));
-     properties =
-         new DecimalFormatProperties()
-             .setMinimumExponentDigits(1)
-             .setMaximumIntegerDigits(3)
-             .setMaximumFractionDigits(1);
-     formats.add(NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH));
-     properties = new DecimalFormatProperties().setRoundingIncrement(new BigDecimal("0.5"));
-     formats.add(NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH));
-     String[] cases = {
-       "1.0",
-       "2.01",
-       "1234.56",
-       "3000.0",
-       "0.00026418",
-       "0.01789261",
-       "468160.0",
-       "999000.0",
-       "999900.0",
-       "999990.0",
-       "0.0",
-       "12345678901.0",
-       "-5193.48",
-     };
-     String[] hardCases = {
-       "9999999999999900.0",
-       "789000000000000000000000.0",
-       "789123123567853156372158.0",
-       "987654321987654321987654321987654321987654311987654321.0",
-     };
-     String[] doubleCases = {
-       "512.0000000000017",
-       "4095.9999999999977",
-       "4095.999999999998",
-       "4095.9999999999986",
-       "4095.999999999999",
-       "4095.9999999999995",
-       "4096.000000000001",
-       "4096.000000000002",
-       "4096.000000000003",
-       "4096.000000000004",
-       "4096.000000000005",
-       "4096.0000000000055",
-       "4096.000000000006",
-       "4096.000000000007",
-     };
-     int i = 0;
-     for (String str : cases) {
-       testDecimalQuantity(i++, str, formats, 0);
+     @Ignore
+     @Test
+     public void testBehavior() throws ParseException {
+         // Make a list of several formatters to test the behavior of DecimalQuantity.
+         List<LocalizedNumberFormatter> formats = new ArrayList<LocalizedNumberFormatter>();
+         DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(ULocale.ENGLISH);
+         DecimalFormatProperties properties = new DecimalFormatProperties();
+         formats.add(
+                 NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH));
+         properties = new DecimalFormatProperties().setMinimumSignificantDigits(3)
+                 .setMaximumSignificantDigits(3).setCompactStyle(CompactStyle.LONG);
+         formats.add(
+                 NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH));
+         properties = new DecimalFormatProperties().setMinimumExponentDigits(1).setMaximumIntegerDigits(3)
+                 .setMaximumFractionDigits(1);
+         formats.add(
+                 NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH));
+         properties = new DecimalFormatProperties().setRoundingIncrement(new BigDecimal("0.5"));
+         formats.add(
+                 NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH));
+         String[] cases = {
+                 "1.0",
+                 "2.01",
+                 "1234.56",
+                 "3000.0",
+                 "0.00026418",
+                 "0.01789261",
+                 "468160.0",
+                 "999000.0",
+                 "999900.0",
+                 "999990.0",
+                 "0.0",
+                 "12345678901.0",
+                 "-5193.48", };
+         String[] hardCases = {
+                 "9999999999999900.0",
+                 "789000000000000000000000.0",
+                 "789123123567853156372158.0",
+                 "987654321987654321987654321987654321987654311987654321.0", };
+         String[] doubleCases = {
+                 "512.0000000000017",
+                 "4095.9999999999977",
+                 "4095.999999999998",
+                 "4095.9999999999986",
+                 "4095.999999999999",
+                 "4095.9999999999995",
+                 "4096.000000000001",
+                 "4096.000000000002",
+                 "4096.000000000003",
+                 "4096.000000000004",
+                 "4096.000000000005",
+                 "4096.0000000000055",
+                 "4096.000000000006",
+                 "4096.000000000007", };
+         int i = 0;
+         for (String str : cases) {
+             testDecimalQuantity(i++, str, formats, 0);
+         }
+         i = 0;
+         for (String str : hardCases) {
+             testDecimalQuantity(i++, str, formats, 1);
+         }
+         i = 0;
+         for (String str : doubleCases) {
+             testDecimalQuantity(i++, str, formats, 2);
+         }
      }
  
-     i = 0;
-     for (String str : hardCases) {
-       testDecimalQuantity(i++, str, formats, 1);
+     static void testDecimalQuantity(
+             int t,
+             String str,
+             List<LocalizedNumberFormatter> formats,
+             int mode) {
+         if (mode == 2) {
+             assertEquals("Double is not valid", Double.toString(Double.parseDouble(str)), str);
+         }
+         List<DecimalQuantity> qs = new ArrayList<DecimalQuantity>();
+         BigDecimal d = new BigDecimal(str);
+         qs.add(new DecimalQuantity_SimpleStorage(d));
+         if (mode == 0)
+             qs.add(new DecimalQuantity_64BitBCD(d));
+         qs.add(new DecimalQuantity_ByteArrayBCD(d));
+         qs.add(new DecimalQuantity_DualStorageBCD(d));
+         if (new BigDecimal(Double.toString(d.doubleValue())).compareTo(d) == 0) {
+             double dv = d.doubleValue();
+             qs.add(new DecimalQuantity_SimpleStorage(dv));
+             if (mode == 0)
+                 qs.add(new DecimalQuantity_64BitBCD(dv));
+             qs.add(new DecimalQuantity_ByteArrayBCD(dv));
+             qs.add(new DecimalQuantity_DualStorageBCD(dv));
+         }
+         if (new BigDecimal(Long.toString(d.longValue())).compareTo(d) == 0) {
+             double lv = d.longValue();
+             qs.add(new DecimalQuantity_SimpleStorage(lv));
+             if (mode == 0)
+                 qs.add(new DecimalQuantity_64BitBCD(lv));
+             qs.add(new DecimalQuantity_ByteArrayBCD(lv));
+             qs.add(new DecimalQuantity_DualStorageBCD(lv));
+         }
+         testDecimalQuantityExpectedOutput(qs.get(0), str);
+         if (qs.size() == 1) {
+             return;
+         }
+         for (int i = 1; i < qs.size(); i++) {
+             DecimalQuantity q0 = qs.get(0);
+             DecimalQuantity q1 = qs.get(i);
+             testDecimalQuantityExpectedOutput(q1, str);
+             testDecimalQuantityRounding(q0, q1);
+             testDecimalQuantityRoundingInterval(q0, q1);
+             testDecimalQuantityMath(q0, q1);
+             testDecimalQuantityWithFormats(q0, q1, formats);
+         }
      }
  
-     i = 0;
-     for (String str : doubleCases) {
-       testDecimalQuantity(i++, str, formats, 2);
+     private static void testDecimalQuantityExpectedOutput(DecimalQuantity rq, String expected) {
+         DecimalQuantity q0 = rq.createCopy();
+         // Force an accurate double
+         q0.roundToInfinity();
+         q0.setIntegerLength(1, Integer.MAX_VALUE);
+         q0.setFractionLength(1, Integer.MAX_VALUE);
+         String actual = q0.toPlainString();
+         assertEquals("Unexpected output from simple string conversion (" + q0 + ")", expected, actual);
      }
-   }
  
-   static void testDecimalQuantity(int t, String str, List<LocalizedNumberFormatter> formats, int mode) {
-     if (mode == 2) {
-       assertEquals("Double is not valid", Double.toString(Double.parseDouble(str)), str);
+     private static final MathContext MATH_CONTEXT_HALF_EVEN = new MathContext(0, RoundingMode.HALF_EVEN);
+     private static final MathContext MATH_CONTEXT_CEILING = new MathContext(0, RoundingMode.CEILING);
++    private static final MathContext MATH_CONTEXT_FLOOR = new MathContext(0, RoundingMode.FLOOR);
+     private static final MathContext MATH_CONTEXT_PRECISION = new MathContext(3, RoundingMode.HALF_UP);
+     private static void testDecimalQuantityRounding(DecimalQuantity rq0, DecimalQuantity rq1) {
+         DecimalQuantity q0 = rq0.createCopy();
+         DecimalQuantity q1 = rq1.createCopy();
+         q0.roundToMagnitude(-1, MATH_CONTEXT_HALF_EVEN);
+         q1.roundToMagnitude(-1, MATH_CONTEXT_HALF_EVEN);
+         testDecimalQuantityBehavior(q0, q1);
+         q0 = rq0.createCopy();
+         q1 = rq1.createCopy();
+         q0.roundToMagnitude(-1, MATH_CONTEXT_CEILING);
+         q1.roundToMagnitude(-1, MATH_CONTEXT_CEILING);
+         testDecimalQuantityBehavior(q0, q1);
+         q0 = rq0.createCopy();
+         q1 = rq1.createCopy();
+         q0.roundToMagnitude(-1, MATH_CONTEXT_PRECISION);
+         q1.roundToMagnitude(-1, MATH_CONTEXT_PRECISION);
+         testDecimalQuantityBehavior(q0, q1);
      }
  
-     List<DecimalQuantity> qs = new ArrayList<DecimalQuantity>();
-     BigDecimal d = new BigDecimal(str);
-     qs.add(new DecimalQuantity_SimpleStorage(d));
-     if (mode == 0) qs.add(new DecimalQuantity_64BitBCD(d));
-     qs.add(new DecimalQuantity_ByteArrayBCD(d));
-     qs.add(new DecimalQuantity_DualStorageBCD(d));
-     if (new BigDecimal(Double.toString(d.doubleValue())).compareTo(d) == 0) {
-       double dv = d.doubleValue();
-       qs.add(new DecimalQuantity_SimpleStorage(dv));
-       if (mode == 0) qs.add(new DecimalQuantity_64BitBCD(dv));
-       qs.add(new DecimalQuantity_ByteArrayBCD(dv));
-       qs.add(new DecimalQuantity_DualStorageBCD(dv));
+     private static void testDecimalQuantityRoundingInterval(DecimalQuantity rq0, DecimalQuantity rq1) {
+         DecimalQuantity q0 = rq0.createCopy();
+         DecimalQuantity q1 = rq1.createCopy();
+         q0.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_HALF_EVEN);
+         q1.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_HALF_EVEN);
+         testDecimalQuantityBehavior(q0, q1);
+         q0 = rq0.createCopy();
+         q1 = rq1.createCopy();
+         q0.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_CEILING);
+         q1.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_CEILING);
+         testDecimalQuantityBehavior(q0, q1);
      }
  
-     if (new BigDecimal(Long.toString(d.longValue())).compareTo(d) == 0) {
-       double lv = d.longValue();
-       qs.add(new DecimalQuantity_SimpleStorage(lv));
-       if (mode == 0) qs.add(new DecimalQuantity_64BitBCD(lv));
-       qs.add(new DecimalQuantity_ByteArrayBCD(lv));
-       qs.add(new DecimalQuantity_DualStorageBCD(lv));
+     private static void testDecimalQuantityMath(DecimalQuantity rq0, DecimalQuantity rq1) {
+         DecimalQuantity q0 = rq0.createCopy();
+         DecimalQuantity q1 = rq1.createCopy();
+         q0.adjustMagnitude(-3);
+         q1.adjustMagnitude(-3);
+         testDecimalQuantityBehavior(q0, q1);
+         q0 = rq0.createCopy();
+         q1 = rq1.createCopy();
+         q0.multiplyBy(new BigDecimal("3.14159"));
+         q1.multiplyBy(new BigDecimal("3.14159"));
+         testDecimalQuantityBehavior(q0, q1);
      }
  
-     testDecimalQuantityExpectedOutput(qs.get(0), str);
-     if (qs.size() == 1) {
-       return;
+     private static void testDecimalQuantityWithFormats(
+             DecimalQuantity rq0,
+             DecimalQuantity rq1,
+             List<LocalizedNumberFormatter> formats) {
+         for (LocalizedNumberFormatter format : formats) {
+             DecimalQuantity q0 = rq0.createCopy();
+             DecimalQuantity q1 = rq1.createCopy();
+             String s1 = format.format(q0).toString();
+             String s2 = format.format(q1).toString();
+             assertEquals("Different output from formatter (" + q0 + ", " + q1 + ")", s1, s2);
+         }
      }
  
-     for (int i = 1; i < qs.size(); i++) {
-       DecimalQuantity q0 = qs.get(0);
-       DecimalQuantity q1 = qs.get(i);
-       testDecimalQuantityExpectedOutput(q1, str);
-       testDecimalQuantityRounding(q0, q1);
-       testDecimalQuantityRoundingInterval(q0, q1);
-       testDecimalQuantityMath(q0, q1);
-       testDecimalQuantityWithFormats(q0, q1, formats);
+     private static void testDecimalQuantityBehavior(DecimalQuantity rq0, DecimalQuantity rq1) {
+         DecimalQuantity q0 = rq0.createCopy();
+         DecimalQuantity q1 = rq1.createCopy();
+         assertEquals("Different sign (" + q0 + ", " + q1 + ")", q0.isNegative(), q1.isNegative());
+         assertEquals("Different fingerprint (" + q0 + ", " + q1 + ")",
+                 q0.getPositionFingerprint(),
+                 q1.getPositionFingerprint());
+         assertDoubleEquals("Different double values (" + q0 + ", " + q1 + ")",
+                 q0.toDouble(),
+                 q1.toDouble());
+         assertBigDecimalEquals("Different BigDecimal values (" + q0 + ", " + q1 + ")",
+                 q0.toBigDecimal(),
+                 q1.toBigDecimal());
+         q0.roundToInfinity();
+         q1.roundToInfinity();
+         assertEquals("Different lower display magnitude",
+                 q0.getLowerDisplayMagnitude(),
+                 q1.getLowerDisplayMagnitude());
+         assertEquals("Different upper display magnitude",
+                 q0.getUpperDisplayMagnitude(),
+                 q1.getUpperDisplayMagnitude());
+         for (int m = q0.getUpperDisplayMagnitude(); m >= q0.getLowerDisplayMagnitude(); m--) {
+             assertEquals("Different digit at magnitude " + m + " (" + q0 + ", " + q1 + ")",
+                     q0.getDigit(m),
+                     q1.getDigit(m));
+         }
+         if (rq0 instanceof DecimalQuantity_DualStorageBCD) {
+             String message = ((DecimalQuantity_DualStorageBCD) rq0).checkHealth();
+             if (message != null)
+                 errln(message);
+         }
+         if (rq1 instanceof DecimalQuantity_DualStorageBCD) {
+             String message = ((DecimalQuantity_DualStorageBCD) rq1).checkHealth();
+             if (message != null)
+                 errln(message);
+         }
      }
-   }
-   private static void testDecimalQuantityExpectedOutput(DecimalQuantity rq, String expected) {
-     DecimalQuantity q0 = rq.createCopy();
-     // Force an accurate double
-     q0.roundToInfinity();
-     q0.setIntegerLength(1, Integer.MAX_VALUE);
-     q0.setFractionLength(1, Integer.MAX_VALUE);
-     String actual = q0.toPlainString();
-     assertEquals("Unexpected output from simple string conversion (" + q0 + ")", expected, actual);
-   }
-   private static final MathContext MATH_CONTEXT_HALF_EVEN =
-       new MathContext(0, RoundingMode.HALF_EVEN);
-   private static final MathContext MATH_CONTEXT_CEILING = new MathContext(0, RoundingMode.CEILING);
-   private static final MathContext MATH_CONTEXT_FLOOR = new MathContext(0, RoundingMode.FLOOR);
-   private static final MathContext MATH_CONTEXT_PRECISION =
-       new MathContext(3, RoundingMode.HALF_UP);
-   private static void testDecimalQuantityRounding(DecimalQuantity rq0, DecimalQuantity rq1) {
-     DecimalQuantity q0 = rq0.createCopy();
-     DecimalQuantity q1 = rq1.createCopy();
-     q0.roundToMagnitude(-1, MATH_CONTEXT_HALF_EVEN);
-     q1.roundToMagnitude(-1, MATH_CONTEXT_HALF_EVEN);
-     testDecimalQuantityBehavior(q0, q1);
-     q0 = rq0.createCopy();
-     q1 = rq1.createCopy();
-     q0.roundToMagnitude(-1, MATH_CONTEXT_CEILING);
-     q1.roundToMagnitude(-1, MATH_CONTEXT_CEILING);
-     testDecimalQuantityBehavior(q0, q1);
-     q0 = rq0.createCopy();
-     q1 = rq1.createCopy();
-     q0.roundToMagnitude(-1, MATH_CONTEXT_PRECISION);
-     q1.roundToMagnitude(-1, MATH_CONTEXT_PRECISION);
-     testDecimalQuantityBehavior(q0, q1);
-     q0 = rq0.createCopy();
-     q1 = rq1.createCopy();
-     q0.roundToMagnitude(0, MATH_CONTEXT_FLOOR);
-     q1.truncate();
-     testDecimalQuantityBehavior(q0, q1);
-     q0 = rq0.createCopy();
-     q1 = rq1.createCopy();
-     q0.truncate();
-     q1.roundToMagnitude(0, MATH_CONTEXT_FLOOR);
-     testDecimalQuantityBehavior(q0, q1);
-   }
-   private static void testDecimalQuantityRoundingInterval(DecimalQuantity rq0, DecimalQuantity rq1) {
-     DecimalQuantity q0 = rq0.createCopy();
-     DecimalQuantity q1 = rq1.createCopy();
-     q0.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_HALF_EVEN);
-     q1.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_HALF_EVEN);
-     testDecimalQuantityBehavior(q0, q1);
-     q0 = rq0.createCopy();
-     q1 = rq1.createCopy();
-     q0.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_CEILING);
-     q1.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_CEILING);
-     testDecimalQuantityBehavior(q0, q1);
-   }
-   private static void testDecimalQuantityMath(DecimalQuantity rq0, DecimalQuantity rq1) {
-     DecimalQuantity q0 = rq0.createCopy();
-     DecimalQuantity q1 = rq1.createCopy();
-     q0.adjustMagnitude(-3);
-     q1.adjustMagnitude(-3);
-     testDecimalQuantityBehavior(q0, q1);
-     q0 = rq0.createCopy();
-     q1 = rq1.createCopy();
-     q0.multiplyBy(new BigDecimal("3.14159"));
-     q1.multiplyBy(new BigDecimal("3.14159"));
-     testDecimalQuantityBehavior(q0, q1);
-   }
  
-   private static void testDecimalQuantityWithFormats(
-       DecimalQuantity rq0, DecimalQuantity rq1, List<LocalizedNumberFormatter> formats) {
-     for (LocalizedNumberFormatter format : formats) {
-       DecimalQuantity q0 = rq0.createCopy();
-       DecimalQuantity q1 = rq1.createCopy();
-       String s1 = format.format(q0).toString();
-       String s2 = format.format(q1).toString();
-       assertEquals("Different output from formatter (" + q0 + ", " + q1 + ")", s1, s2);
+     @Test
+     public void testSwitchStorage() {
+         DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD();
+         fq.setToLong(1234123412341234L);
+         assertFalse("Should not be using byte array", fq.isUsingBytes());
+         assertEquals("Failed on initialize", "1234123412341234E0", fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
+         // Long -> Bytes
+         fq.appendDigit((byte) 5, 0, true);
+         assertTrue("Should be using byte array", fq.isUsingBytes());
+         assertEquals("Failed on multiply", "12341234123412345E0", fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
+         // Bytes -> Long
+         fq.roundToMagnitude(5, MATH_CONTEXT_HALF_EVEN);
+         assertFalse("Should not be using byte array", fq.isUsingBytes());
+         assertEquals("Failed on round", "123412341234E5", fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
      }
-   }
  
-   private static void testDecimalQuantityBehavior(DecimalQuantity rq0, DecimalQuantity rq1) {
-     DecimalQuantity q0 = rq0.createCopy();
-     DecimalQuantity q1 = rq1.createCopy();
-     assertEquals("Different sign (" + q0 + ", " + q1 + ")", q0.isNegative(), q1.isNegative());
-     assertEquals(
-         "Different fingerprint (" + q0 + ", " + q1 + ")",
-         q0.getPositionFingerprint(),
-         q1.getPositionFingerprint());
-     assertDoubleEquals(
-         "Different double values (" + q0 + ", " + q1 + ")", q0.toDouble(), q1.toDouble());
-     assertBigDecimalEquals(
-         "Different BigDecimal values (" + q0 + ", " + q1 + ")",
-         q0.toBigDecimal(),
-         q1.toBigDecimal());
-     q0.roundToInfinity();
-     q1.roundToInfinity();
-     assertEquals(
-         "Different lower display magnitude",
-         q0.getLowerDisplayMagnitude(),
-         q1.getLowerDisplayMagnitude());
-     assertEquals(
-         "Different upper display magnitude",
-         q0.getUpperDisplayMagnitude(),
-         q1.getUpperDisplayMagnitude());
-     for (int m = q0.getUpperDisplayMagnitude(); m >= q0.getLowerDisplayMagnitude(); m--) {
-       assertEquals(
-           "Different digit at magnitude " + m + " (" + q0 + ", " + q1 + ")",
-           q0.getDigit(m),
-           q1.getDigit(m));
+     @Test
+     public void testAppend() {
+         DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD();
+         fq.appendDigit((byte) 1, 0, true);
+         assertEquals("Failed on append", "1E0", fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
+         fq.appendDigit((byte) 2, 0, true);
+         assertEquals("Failed on append", "12E0", fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
+         fq.appendDigit((byte) 3, 1, true);
+         assertEquals("Failed on append", "1203E0", fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
+         fq.appendDigit((byte) 0, 1, true);
+         assertEquals("Failed on append", "1203E2", fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
+         fq.appendDigit((byte) 4, 0, true);
+         assertEquals("Failed on append", "1203004E0", fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
+         fq.appendDigit((byte) 0, 0, true);
+         assertEquals("Failed on append", "1203004E1", fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
+         fq.appendDigit((byte) 5, 0, false);
+         assertEquals("Failed on append", "120300405E-1", fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
+         fq.appendDigit((byte) 6, 0, false);
+         assertEquals("Failed on append", "1203004056E-2", fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
+         fq.appendDigit((byte) 7, 3, false);
+         assertEquals("Failed on append", "12030040560007E-6", fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
+         StringBuilder baseExpected = new StringBuilder("12030040560007");
+         for (int i = 0; i < 10; i++) {
+             fq.appendDigit((byte) 8, 0, false);
+             baseExpected.append('8');
+             StringBuilder expected = new StringBuilder(baseExpected);
+             expected.append("E");
+             expected.append(-7 - i);
+             assertEquals("Failed on append", expected.toString(), fq.toNumberString());
+             assertNull("Failed health check", fq.checkHealth());
+         }
+         fq.appendDigit((byte) 9, 2, false);
+         baseExpected.append("009");
+         StringBuilder expected = new StringBuilder(baseExpected);
+         expected.append('E');
+         expected.append("-19");
+         assertEquals("Failed on append", expected.toString(), fq.toNumberString());
+         assertNull("Failed health check", fq.checkHealth());
      }
  
-     if (rq0 instanceof DecimalQuantity_DualStorageBCD) {
-       String message = ((DecimalQuantity_DualStorageBCD) rq0).checkHealth();
-       if (message != null) errln(message);
-     }
-     if (rq1 instanceof DecimalQuantity_DualStorageBCD) {
-       String message = ((DecimalQuantity_DualStorageBCD) rq1).checkHealth();
-       if (message != null) errln(message);
+     @Ignore
+     @Test
+     public void testConvertToAccurateDouble() {
+         // based on https://github.com/google/double-conversion/issues/28
+         double[] hardDoubles = {
+                 1651087494906221570.0,
+                 -5074790912492772E-327,
+                 83602530019752571E-327,
+                 2.207817077636718750000000000000,
+                 1.818351745605468750000000000000,
+                 3.941719055175781250000000000000,
+                 3.738609313964843750000000000000,
+                 3.967735290527343750000000000000,
+                 1.328025817871093750000000000000,
+                 3.920967102050781250000000000000,
+                 1.015235900878906250000000000000,
+                 1.335227966308593750000000000000,
+                 1.344520568847656250000000000000,
+                 2.879127502441406250000000000000,
+                 3.695838928222656250000000000000,
+                 1.845344543457031250000000000000,
+                 3.793952941894531250000000000000,
+                 3.211402893066406250000000000000,
+                 2.565971374511718750000000000000,
+                 0.965156555175781250000000000000,
+                 2.700004577636718750000000000000,
+                 0.767097473144531250000000000000,
+                 1.780448913574218750000000000000,
+                 2.624839782714843750000000000000,
+                 1.305290222167968750000000000000,
+                 3.834922790527343750000000000000, };
+         double[] integerDoubles = {
+                 51423,
+                 51423e10,
+                 4.503599627370496E15,
+                 6.789512076111555E15,
+                 9.007199254740991E15,
+                 9.007199254740992E15 };
+         for (double d : hardDoubles) {
+             checkDoubleBehavior(d, true, "");
+         }
+         for (double d : integerDoubles) {
+             checkDoubleBehavior(d, false, "");
+         }
+         assertEquals("NaN check failed",
+                 Double.NaN,
+                 new DecimalQuantity_DualStorageBCD(Double.NaN).toDouble());
+         assertEquals("Inf check failed",
+                 Double.POSITIVE_INFINITY,
+                 new DecimalQuantity_DualStorageBCD(Double.POSITIVE_INFINITY).toDouble());
+         assertEquals("-Inf check failed",
+                 Double.NEGATIVE_INFINITY,
+                 new DecimalQuantity_DualStorageBCD(Double.NEGATIVE_INFINITY).toDouble());
+         // Generate random doubles
+         String alert = "UNEXPECTED FAILURE: PLEASE REPORT THIS MESSAGE TO THE ICU TEAM: ";
+         Random rnd = new Random();
+         for (int i = 0; i < 10000; i++) {
+             double d = Double.longBitsToDouble(rnd.nextLong());
+             if (Double.isNaN(d) || Double.isInfinite(d))
+                 continue;
+             checkDoubleBehavior(d, false, alert);
+         }
      }
-   }
  
-   @Test
-   public void testSwitchStorage() {
-     DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD();
-     fq.setToLong(1234123412341234L);
-     assertFalse("Should not be using byte array", fq.isUsingBytes());
-     assertEquals("Failed on initialize", "1234123412341234E0", fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-     // Long -> Bytes
-     fq.appendDigit((byte) 5, 0, true);
-     assertTrue("Should be using byte array", fq.isUsingBytes());
-     assertEquals("Failed on multiply", "12341234123412345E0", fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-     // Bytes -> Long
-     fq.roundToMagnitude(5, MATH_CONTEXT_HALF_EVEN);
-     assertFalse("Should not be using byte array", fq.isUsingBytes());
-     assertEquals("Failed on round", "123412341234E5", fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-   }
-   @Test
-   public void testAppend() {
-     DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD();
-     fq.appendDigit((byte) 1, 0, true);
-     assertEquals("Failed on append", "1E0", fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-     fq.appendDigit((byte) 2, 0, true);
-     assertEquals("Failed on append", "12E0", fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-     fq.appendDigit((byte) 3, 1, true);
-     assertEquals("Failed on append", "1203E0", fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-     fq.appendDigit((byte) 0, 1, true);
-     assertEquals("Failed on append", "1203E2", fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-     fq.appendDigit((byte) 4, 0, true);
-     assertEquals("Failed on append", "1203004E0", fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-     fq.appendDigit((byte) 0, 0, true);
-     assertEquals("Failed on append", "1203004E1", fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-     fq.appendDigit((byte) 5, 0, false);
-     assertEquals("Failed on append", "120300405E-1", fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-     fq.appendDigit((byte) 6, 0, false);
-     assertEquals("Failed on append", "1203004056E-2", fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-     fq.appendDigit((byte) 7, 3, false);
-     assertEquals("Failed on append", "12030040560007E-6", fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-     StringBuilder baseExpected = new StringBuilder("12030040560007");
-     for (int i = 0; i < 10; i++) {
-       fq.appendDigit((byte) 8, 0, false);
-       baseExpected.append('8');
-       StringBuilder expected = new StringBuilder(baseExpected);
-       expected.append("E");
-       expected.append(-7 - i);
-       assertEquals("Failed on append", expected.toString(), fq.toNumberString());
-       assertNull("Failed health check", fq.checkHealth());
+     private static void checkDoubleBehavior(double d, boolean explicitRequired, String alert) {
+         DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD(d);
+         if (explicitRequired) {
+             assertTrue(alert + "Should be using approximate double", !fq.explicitExactDouble);
+         }
+         assertEquals(alert + "Initial construction from hard double", d, fq.toDouble());
+         fq.roundToInfinity();
+         if (explicitRequired) {
+             assertTrue(alert + "Should not be using approximate double", fq.explicitExactDouble);
+         }
+         assertDoubleEquals(alert + "After conversion to exact BCD (double)", d, fq.toDouble());
+         assertBigDecimalEquals(alert + "After conversion to exact BCD (BigDecimal)",
+                 new BigDecimal(Double.toString(d)),
+                 fq.toBigDecimal());
      }
-     fq.appendDigit((byte) 9, 2, false);
-     baseExpected.append("009");
-     StringBuilder expected = new StringBuilder(baseExpected);
-     expected.append('E');
-     expected.append("-19");
-     assertEquals("Failed on append", expected.toString(), fq.toNumberString());
-     assertNull("Failed health check", fq.checkHealth());
-   }
  
-   @Ignore
-   @Test
-   public void testConvertToAccurateDouble() {
-     // based on https://github.com/google/double-conversion/issues/28
-     double[] hardDoubles = {
-       1651087494906221570.0,
-       -5074790912492772E-327,
-       83602530019752571E-327,
-       2.207817077636718750000000000000,
-       1.818351745605468750000000000000,
-       3.941719055175781250000000000000,
-       3.738609313964843750000000000000,
-       3.967735290527343750000000000000,
-       1.328025817871093750000000000000,
-       3.920967102050781250000000000000,
-       1.015235900878906250000000000000,
-       1.335227966308593750000000000000,
-       1.344520568847656250000000000000,
-       2.879127502441406250000000000000,
-       3.695838928222656250000000000000,
-       1.845344543457031250000000000000,
-       3.793952941894531250000000000000,
-       3.211402893066406250000000000000,
-       2.565971374511718750000000000000,
-       0.965156555175781250000000000000,
-       2.700004577636718750000000000000,
-       0.767097473144531250000000000000,
-       1.780448913574218750000000000000,
-       2.624839782714843750000000000000,
-       1.305290222167968750000000000000,
-       3.834922790527343750000000000000,
-     };
-     double[] integerDoubles = {
-       51423,
-       51423e10,
-       4.503599627370496E15,
-       6.789512076111555E15,
-       9.007199254740991E15,
-       9.007199254740992E15
-     };
-     for (double d : hardDoubles) {
-       checkDoubleBehavior(d, true, "");
+     @Test
+     public void testUseApproximateDoubleWhenAble() {
+         Object[][] cases = {
+                 { 1.2345678, 1, MATH_CONTEXT_HALF_EVEN, false },
+                 { 1.2345678, 7, MATH_CONTEXT_HALF_EVEN, false },
+                 { 1.2345678, 12, MATH_CONTEXT_HALF_EVEN, false },
+                 { 1.2345678, 13, MATH_CONTEXT_HALF_EVEN, true },
+                 { 1.235, 1, MATH_CONTEXT_HALF_EVEN, false },
+                 { 1.235, 2, MATH_CONTEXT_HALF_EVEN, true },
+                 { 1.235, 3, MATH_CONTEXT_HALF_EVEN, false },
+                 { 1.000000000000001, 0, MATH_CONTEXT_HALF_EVEN, false },
+                 { 1.000000000000001, 0, MATH_CONTEXT_CEILING, true },
+                 { 1.235, 1, MATH_CONTEXT_CEILING, false },
+                 { 1.235, 2, MATH_CONTEXT_CEILING, false },
+                 { 1.235, 3, MATH_CONTEXT_CEILING, true } };
+         for (Object[] cas : cases) {
+             double d = (Double) cas[0];
+             int maxFrac = (Integer) cas[1];
+             MathContext mc = (MathContext) cas[2];
+             boolean usesExact = (Boolean) cas[3];
+             DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD(d);
+             assertTrue("Should be using approximate double", !fq.explicitExactDouble);
+             fq.roundToMagnitude(-maxFrac, mc);
+             assertEquals(
+                     "Using approximate double after rounding: " + d + " maxFrac=" + maxFrac + " " + mc,
+                     usesExact,
+                     fq.explicitExactDouble);
+         }
      }
  
-     for (double d : integerDoubles) {
-       checkDoubleBehavior(d, false, "");
+     @Test
+     public void testDecimalQuantityBehaviorStandalone() {
+         DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD();
+         assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 0E0>");
+         fq.setToInt(51423);
+         assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 51423E0>");
+         fq.adjustMagnitude(-3);
+         assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 51423E-3>");
+         fq.setToLong(999999999999000L);
+         assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 999999999999E3>");
+         fq.setIntegerLength(2, 5);
+         assertToStringAndHealth(fq, "<DecimalQuantity 5:2:0:-999 long 999999999999E3>");
+         fq.setFractionLength(3, 6);
+         assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 999999999999E3>");
+         fq.setToDouble(987.654321);
+         assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 987654321E-6>");
+         fq.roundToInfinity();
+         assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 987654321E-6>");
+         fq.roundToIncrement(new BigDecimal("0.005"), MATH_CONTEXT_HALF_EVEN);
+         assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 987655E-3>");
+         fq.roundToMagnitude(-2, MATH_CONTEXT_HALF_EVEN);
+         assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 98766E-2>");
      }
  
-     assertEquals("NaN check failed", Double.NaN, new DecimalQuantity_DualStorageBCD(Double.NaN).toDouble());
-     assertEquals(
-         "Inf check failed",
-         Double.POSITIVE_INFINITY,
-         new DecimalQuantity_DualStorageBCD(Double.POSITIVE_INFINITY).toDouble());
-     assertEquals(
-         "-Inf check failed",
-         Double.NEGATIVE_INFINITY,
-         new DecimalQuantity_DualStorageBCD(Double.NEGATIVE_INFINITY).toDouble());
-     // Generate random doubles
-     String alert = "UNEXPECTED FAILURE: PLEASE REPORT THIS MESSAGE TO THE ICU TEAM: ";
-     Random rnd = new Random();
-     for (int i = 0; i < 10000; i++) {
-       double d = Double.longBitsToDouble(rnd.nextLong());
-       if (Double.isNaN(d) || Double.isInfinite(d)) continue;
-       checkDoubleBehavior(d, false, alert);
-     }
-   }
-   private static void checkDoubleBehavior(double d, boolean explicitRequired, String alert) {
-     DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD(d);
-     if (explicitRequired) {
-       assertTrue(alert + "Should be using approximate double", !fq.explicitExactDouble);
-     }
-     assertEquals(alert + "Initial construction from hard double", d, fq.toDouble());
-     fq.roundToInfinity();
-     if (explicitRequired) {
-       assertTrue(alert + "Should not be using approximate double", fq.explicitExactDouble);
-     }
-     assertDoubleEquals(alert + "After conversion to exact BCD (double)", d, fq.toDouble());
-     assertBigDecimalEquals(
-         alert + "After conversion to exact BCD (BigDecimal)",
-         new BigDecimal(Double.toString(d)),
-         fq.toBigDecimal());
-   }
-   @Test
-   public void testUseApproximateDoubleWhenAble() {
-     Object[][] cases = {
-       {1.2345678, 1, MATH_CONTEXT_HALF_EVEN, false},
-       {1.2345678, 7, MATH_CONTEXT_HALF_EVEN, false},
-       {1.2345678, 12, MATH_CONTEXT_HALF_EVEN, false},
-       {1.2345678, 13, MATH_CONTEXT_HALF_EVEN, true},
-       {1.235, 1, MATH_CONTEXT_HALF_EVEN, false},
-       {1.235, 2, MATH_CONTEXT_HALF_EVEN, true},
-       {1.235, 3, MATH_CONTEXT_HALF_EVEN, false},
-       {1.000000000000001, 0, MATH_CONTEXT_HALF_EVEN, false},
-       {1.000000000000001, 0, MATH_CONTEXT_CEILING, true},
-       {1.235, 1, MATH_CONTEXT_CEILING, false},
-       {1.235, 2, MATH_CONTEXT_CEILING, false},
-       {1.235, 3, MATH_CONTEXT_CEILING, true}
-     };
-     for (Object[] cas : cases) {
-       double d = (Double) cas[0];
-       int maxFrac = (Integer) cas[1];
-       MathContext mc = (MathContext) cas[2];
-       boolean usesExact = (Boolean) cas[3];
-       DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD(d);
-       assertTrue("Should be using approximate double", !fq.explicitExactDouble);
-       fq.roundToMagnitude(-maxFrac, mc);
-       assertEquals(
-           "Using approximate double after rounding: " + d + " maxFrac=" + maxFrac + " " + mc,
-           usesExact,
-           fq.explicitExactDouble);
-     }
-   }
-   @Test
-   public void testDecimalQuantityBehaviorStandalone() {
-       DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD();
-       assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 0E0>");
-       fq.setToInt(51423);
-       assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 51423E0>");
-       fq.adjustMagnitude(-3);
-       assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 51423E-3>");
-       fq.setToLong(999999999999000L);
-       assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 999999999999E3>");
-       fq.setIntegerLength(2, 5);
-       assertToStringAndHealth(fq, "<DecimalQuantity 5:2:0:-999 long 999999999999E3>");
-       fq.setFractionLength(3, 6);
-       assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 999999999999E3>");
-       fq.setToDouble(987.654321);
-       assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 987654321E-6>");
-       fq.roundToInfinity();
-       assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 987654321E-6>");
-       fq.roundToIncrement(new BigDecimal("0.005"), MATH_CONTEXT_HALF_EVEN);
-       assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 987655E-3>");
-       fq.roundToMagnitude(-2, MATH_CONTEXT_HALF_EVEN);
-       assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 98766E-2>");
-   }
 +  @Test
 +  public void testFitsInLong() {
 +      DecimalQuantity_DualStorageBCD quantity = new DecimalQuantity_DualStorageBCD();
 +      quantity.setToInt(0);
 +      assertTrue("Zero should fit", quantity.fitsInLong());
 +      quantity.setToInt(42);
 +      assertTrue("Small int should fit", quantity.fitsInLong());
 +      quantity.setToDouble(0.1);
 +      assertFalse("Fraction should not fit", quantity.fitsInLong());
 +      quantity.setToDouble(42.1);
 +      assertFalse("Fraction should not fit", quantity.fitsInLong());
 +      quantity.setToLong(1000000);
 +      assertTrue("Large low-precision int should fit", quantity.fitsInLong());
 +      quantity.setToLong(1000000000000000000L);
 +      assertTrue("10^19 should fit", quantity.fitsInLong());
 +      quantity.setToLong(1234567890123456789L);
 +      assertTrue("A number between 10^19 and max long should fit", quantity.fitsInLong());
 +      quantity.setToLong(9223372026854775808L);
 +      assertTrue("A number less than max long but with similar digits should fit", quantity.fitsInLong());
 +      quantity.setToLong(9223372036854775806L);
 +      assertTrue("One less than max long should fit", quantity.fitsInLong());
 +      quantity.setToLong(9223372036854775807L);
 +      assertTrue("Max long should fit", quantity.fitsInLong());
 +      quantity.setToBigInteger(new BigInteger("9223372036854775808"));
 +      assertFalse("One greater than max long long should not fit", quantity.fitsInLong());
 +      quantity.setToBigInteger(new BigInteger("9223372046854775806"));
 +      assertFalse("A number between max long and 10^20 should not fit", quantity.fitsInLong());
 +      quantity.setToBigInteger(new BigInteger("10000000000000000000"));
 +      assertFalse("10^20 should not fit", quantity.fitsInLong());
 +  }
 +
-   static void assertDoubleEquals(String message, double d1, double d2) {
-     boolean equal = (Math.abs(d1 - d2) < 1e-6) || (Math.abs((d1 - d2) / d1) < 1e-6);
-     handleAssert(equal, message, d1, d2, null, false);
-   }
+     static void assertDoubleEquals(String message, double d1, double d2) {
+         boolean equal = (Math.abs(d1 - d2) < 1e-6) || (Math.abs((d1 - d2) / d1) < 1e-6);
+         handleAssert(equal, message, d1, d2, null, false);
+     }
  
-   static void assertBigDecimalEquals(String message, String d1, BigDecimal d2) {
-     assertBigDecimalEquals(message, new BigDecimal(d1), d2);
-   }
+     static void assertBigDecimalEquals(String message, String d1, BigDecimal d2) {
+         assertBigDecimalEquals(message, new BigDecimal(d1), d2);
+     }
  
-   static void assertBigDecimalEquals(String message, BigDecimal d1, BigDecimal d2) {
-     boolean equal = d1.compareTo(d2) == 0;
-     handleAssert(equal, message, d1, d2, null, false);
-   }
+     static void assertBigDecimalEquals(String message, BigDecimal d1, BigDecimal d2) {
+         boolean equal = d1.compareTo(d2) == 0;
+         handleAssert(equal, message, d1, d2, null, false);
+     }
  
-   static void assertToStringAndHealth(DecimalQuantity_DualStorageBCD fq, String expected) {
-       String actual = fq.toString();
-       assertEquals("DecimalQuantity toString", expected, actual);
-       String health = fq.checkHealth();
-       assertNull("DecimalQuantity health", health);
-   }
+     static void assertToStringAndHealth(DecimalQuantity_DualStorageBCD fq, String expected) {
+         String actual = fq.toString();
+         assertEquals("DecimalQuantity toString", expected, actual);
+         String health = fq.checkHealth();
+         assertNull("DecimalQuantity health", health);
+     }
  }