From 47230019c6e43f5924ca3325aaea3492cfa96a69 Mon Sep 17 00:00:00 2001 From: Elango Cheran Date: Tue, 22 Sep 2020 01:19:29 -0700 Subject: [PATCH] ICU-21270 Update FixedDecimal in Java to support exponent --- .../src/com/ibm/icu/text/PluralRules.java | 97 +++++++++++++++++-- .../icu/dev/test/format/PluralRulesTest.java | 29 ++++++ 2 files changed, 117 insertions(+), 9 deletions(-) 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 0c1a405dff5..9c9289780ce 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 @@ public class PluralRules implements Serializable { final boolean isNegative; + final int exponent; + private final int baseFactor; /** @@ -654,9 +656,10 @@ public class PluralRules implements Serializable { * @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 @@ public class PluralRules implements Serializable { integerValue = n > MAX ? MAX : (long)n; + exponent = e; hasIntegerValue = source == integerValue; // check values. TODO make into unit test. // @@ -694,6 +698,24 @@ public class PluralRules implements Serializable { baseFactor = (int) Math.pow(10, v); } + /** + * @internal CLDR + * @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. @@ -786,6 +808,29 @@ public class PluralRules implements Serializable { } } + /** + * @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. @@ -793,7 +838,28 @@ public class PluralRules implements Serializable { @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 @@ public class PluralRules implements Serializable { 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 @@ public class PluralRules implements Serializable { @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 @@ public class PluralRules implements Serializable { 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 @@ public class PluralRules implements Serializable { @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 @@ public class PluralRules implements Serializable { @Override public int intValue() { // TODO Auto-generated method stub - return (int)integerValue; + return (int) longValue(); } /** @@ -928,7 +1003,11 @@ public class PluralRules implements Serializable { @Deprecated @Override public long longValue() { - return integerValue; + if (exponent == 0) { + return integerValue; + } else { + return (long) (Math.pow(10, exponent) * integerValue); + } } /** @@ -938,7 +1017,7 @@ public class PluralRules implements Serializable { @Deprecated @Override public float floatValue() { - return (float) source; + return (float) (source * Math.pow(10, exponent)); } /** @@ -948,7 +1027,7 @@ public class PluralRules implements Serializable { @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 5c2c3030d7d..b76b55ad753 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 @@ public class PluralRulesTest extends TestFmwk { 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 oldSamples = rules.getSamples(keyword, sampleType);