ICU-21270 Update FixedDecimal in Java to support exponent
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java b/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java
index 0c1a405..9c92897 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java
@@ -562,6 +562,8 @@
 
         final boolean isNegative;
 
+        final int exponent;
+
         private final int baseFactor;
 
         /**
@@ -654,9 +656,10 @@
          * @param v number of digits to the right of the decimal place. e.g 1.00 = 2 25. = 0
          * @param f Corresponds to f in the plural rules grammar.
          *   The digits to the right of the decimal place as an integer. e.g 1.10 = 10
+         * @param e Suppressed exponent for scientific and compact notation
          */
         @Deprecated
-        public FixedDecimal(double n, int v, long f) {
+        public FixedDecimal(double n, int v, long f, int e) {
             isNegative = n < 0;
             source = isNegative ? -n : n;
             visibleDecimalDigitCount = v;
@@ -664,6 +667,7 @@
             integerValue = n > MAX
                     ? MAX
                             : (long)n;
+            exponent = e;
             hasIntegerValue = source == integerValue;
             // check values. TODO make into unit test.
             //
@@ -699,6 +703,24 @@
          * @deprecated This API is ICU internal only.
          */
         @Deprecated
+        public FixedDecimal(double n, int v, long f) {
+            this(n, v, f, 0);
+        }
+
+        /**
+         * @internal CLDR
+         * @deprecated This API is ICU internal only.
+         */
+        @Deprecated
+        public static FixedDecimal createWithExponent(double n, int v, int e) {
+            return new FixedDecimal(n,v,getFractionalDigits(n, v), e);
+        }
+
+        /**
+         * @internal CLDR
+         * @deprecated This API is ICU internal only.
+         */
+        @Deprecated
         public FixedDecimal(double n, int v) {
             this(n,v,getFractionalDigits(n, v));
         }
@@ -788,12 +810,56 @@
 
         /**
          * @internal CLDR
+         * @deprecated This API is ICU internal only
+         */
+        @Deprecated
+        private FixedDecimal (FixedDecimal other) {
+            // Ugly, but necessary, because constructors must only call other
+            // constructors in the first line of the body, and
+            // FixedDecimal(String) was refactored to support exponents.
+            this.source = other.source;
+            this.visibleDecimalDigitCount = other.visibleDecimalDigitCount;
+            this.visibleDecimalDigitCountWithoutTrailingZeros =
+                    other.visibleDecimalDigitCountWithoutTrailingZeros;
+            this.decimalDigits = other.decimalDigits;
+            this.decimalDigitsWithoutTrailingZeros =
+                    other.decimalDigitsWithoutTrailingZeros;
+            this.integerValue = other.integerValue;
+            this.hasIntegerValue = other.hasIntegerValue;
+            this.isNegative = other.isNegative;
+            this.exponent = other.exponent;
+            this.baseFactor = other.baseFactor;
+        }
+
+        /**
+         * @internal CLDR
          * @deprecated This API is ICU internal only.
          */
         @Deprecated
         public FixedDecimal (String n) {
             // Ugly, but for samples we don't care.
-            this(Double.parseDouble(n), getVisibleFractionCount(n));
+            this(parseDecimalSampleRangeNumString(n));
+        }
+
+        /**
+         * @internal CLDR
+         * @deprecated This API is ICU internal only
+         */
+        @Deprecated
+        private static FixedDecimal parseDecimalSampleRangeNumString(String num) {
+            if (num.contains("e")) {
+                int ePos = num.lastIndexOf('e');
+                int expNumPos = ePos + 1;
+                String exponentStr = num.substring(expNumPos);
+                int exponent = Integer.parseInt(exponentStr);
+                String fractionStr = num.substring(0, ePos);
+                return FixedDecimal.createWithExponent(
+                        Double.parseDouble(fractionStr),
+                        getVisibleFractionCount(fractionStr),
+                        exponent);
+            } else {
+                return new FixedDecimal(Double.parseDouble(num), getVisibleFractionCount(num));
+            }
         }
 
         private static int getVisibleFractionCount(String value) {
@@ -822,7 +888,7 @@
             case t: return decimalDigitsWithoutTrailingZeros;
             case v: return visibleDecimalDigitCount;
             case w: return visibleDecimalDigitCountWithoutTrailingZeros;
-            case e: return 0;
+            case e: return exponent;
             default: return source;
             }
         }
@@ -844,6 +910,9 @@
         @Override
         @Deprecated
         public int compareTo(FixedDecimal other) {
+            if (exponent != other.exponent) {
+                return doubleValue() < other.doubleValue() ? -1 : 1;
+            }
             if (integerValue != other.integerValue) {
                 return integerValue < other.integerValue ? -1 : 1;
             }
@@ -877,7 +946,8 @@
                 return false;
             }
             FixedDecimal other = (FixedDecimal)arg0;
-            return source == other.source && visibleDecimalDigitCount == other.visibleDecimalDigitCount && decimalDigits == other.decimalDigits;
+            return source == other.source && visibleDecimalDigitCount == other.visibleDecimalDigitCount && decimalDigits == other.decimalDigits
+                    && exponent == other.exponent;
         }
 
         /**
@@ -898,7 +968,12 @@
         @Deprecated
         @Override
         public String toString() {
-            return String.format(Locale.ROOT, "%." + visibleDecimalDigitCount + "f", source);
+            String baseString = String.format(Locale.ROOT, "%." + visibleDecimalDigitCount + "f", source);
+            if (exponent == 0) {
+                return baseString;
+            } else {
+                return baseString + "e" + exponent;
+            }
         }
 
         /**
@@ -918,7 +993,7 @@
         @Override
         public int intValue() {
             // TODO Auto-generated method stub
-            return (int)integerValue;
+            return (int) longValue();
         }
 
         /**
@@ -928,7 +1003,11 @@
         @Deprecated
         @Override
         public long longValue() {
-            return integerValue;
+            if (exponent == 0) {
+                return integerValue;
+            } else {
+                return (long) (Math.pow(10, exponent) * integerValue);
+            }
         }
 
         /**
@@ -938,7 +1017,7 @@
         @Deprecated
         @Override
         public float floatValue() {
-            return (float) source;
+            return (float) (source * Math.pow(10, exponent));
         }
 
         /**
@@ -948,7 +1027,7 @@
         @Deprecated
         @Override
         public double doubleValue() {
-            return isNegative ? -source : source;
+            return (isNegative ? -source : source) * Math.pow(10, exponent);
         }
 
         /**
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java
index 5c2c303..b76b55a 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java
@@ -190,6 +190,35 @@
         checkOldSamples(description, test, "other", SampleType.DECIMAL, 99d, 99.1, 99.2d, 999d);
     }
 
+    /**
+     * This test is for the support of X.YeZ scientific notation of numbers in
+     * the plural sample string.
+     */
+    @Test
+    public void testSamplesWithExponent() {
+        String description = "one: i = 0,1 @integer 0, 1, 1e5 @decimal 0.0~1.5, 1.1e5; "
+                + "many: e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5"
+                + " @integer 1000000, 2e6, 3e6, 4e6, 5e6, 6e6, 7e6, … @decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …; "
+                + "other:  @integer 2~17, 100, 1000, 10000, 100000, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, …"
+                + " @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …"
+                ;
+        // Creating the PluralRules object means being able to parse numbers
+        // like 1e5 and 1.1e5
+        PluralRules test = PluralRules.createRules(description);
+        checkNewSamples(description, test, "one", PluralRules.SampleType.INTEGER, "@integer 0, 1, 1e5", true,
+                new FixedDecimal(0));
+        checkNewSamples(description, test, "one", PluralRules.SampleType.DECIMAL, "@decimal 0.0~1.5, 1.1e5", true,
+                new FixedDecimal(0, 1));
+        checkNewSamples(description, test, "many", PluralRules.SampleType.INTEGER, "@integer 1000000, 2e6, 3e6, 4e6, 5e6, 6e6, 7e6, …", false,
+                new FixedDecimal(1000000));
+        checkNewSamples(description, test, "many", PluralRules.SampleType.DECIMAL, "@decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …", false,
+                FixedDecimal.createWithExponent(2.1, 1, 6));
+        checkNewSamples(description, test, "other", PluralRules.SampleType.INTEGER, "@integer 2~17, 100, 1000, 10000, 100000, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, …", false,
+                new FixedDecimal(2));
+        checkNewSamples(description, test, "other", PluralRules.SampleType.DECIMAL, "@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …", false,
+                new FixedDecimal(2.0, 1));
+    }
+
     public void checkOldSamples(String description, PluralRules rules, String keyword, SampleType sampleType,
             Double... expected) {
         Collection<Double> oldSamples = rules.getSamples(keyword, sampleType);