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);