ICU-20709 Moving rounder call before number properties.
- Changes EXCEPT_ZERO notation to hide sign on numbers that round to zero.
- Adds additional tests for this behavior.
diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp
index 8127dd8..be0b7c8 100644
--- a/icu4c/source/i18n/number_formatimpl.cpp
+++ b/icu4c/source/i18n/number_formatimpl.cpp
@@ -111,7 +111,6 @@
return;
}
fMicroPropsGenerator->processQuantity(inValue, microsOut, status);
- microsOut.rounder.apply(inValue, status);
microsOut.integerWidth.apply(inValue, status);
}
@@ -124,7 +123,6 @@
return fMicros; // must always return a value
}
fMicroPropsGenerator->processQuantity(inValue, fMicros, status);
- fMicros.rounder.apply(inValue, status);
fMicros.integerWidth.apply(inValue, status);
return fMicros;
}
diff --git a/icu4c/source/i18n/number_patternmodifier.cpp b/icu4c/source/i18n/number_patternmodifier.cpp
index 10b20c6..892ac37 100644
--- a/icu4c/source/i18n/number_patternmodifier.cpp
+++ b/icu4c/source/i18n/number_patternmodifier.cpp
@@ -124,6 +124,7 @@
void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroProps& micros,
UErrorCode& status) const {
parent->processQuantity(quantity, micros, status);
+ micros.rounder.apply(quantity, status);
if (micros.modMiddle != nullptr) {
return;
}
@@ -162,6 +163,7 @@
void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& micros,
UErrorCode& status) const {
fParent->processQuantity(fq, micros, status);
+ micros.rounder.apply(fq, status);
if (micros.modMiddle != nullptr) {
return;
}
diff --git a/icu4c/source/i18n/unicode/unumberformatter.h b/icu4c/source/i18n/unicode/unumberformatter.h
index b27507f..1d6b460 100644
--- a/icu4c/source/i18n/unicode/unumberformatter.h
+++ b/icu4c/source/i18n/unicode/unumberformatter.h
@@ -336,7 +336,7 @@
/**
* Show the minus sign on negative numbers and the plus sign on positive numbers. Do not show a
- * sign on zero or NaN, unless the sign bit is set (-0.0 gets a sign).
+ * sign on zero, numbers that round to zero, or NaN.
*
* @stable ICU 61
*/
@@ -344,9 +344,8 @@
/**
* Use the locale-dependent accounting format on negative numbers, and show the plus sign on
- * positive numbers. Do not show a sign on zero or NaN, unless the sign bit is set (-0.0 gets a
- * sign). For more information on the accounting format, see the ACCOUNTING sign display
- * strategy.
+ * positive numbers. Do not show a sign on zero, numbers that round to zero, or NaN. For more
+ * information on the accounting format, see the ACCOUNTING sign display strategy.
*
* @stable ICU 61
*/
diff --git a/icu4c/source/test/intltest/numbertest.h b/icu4c/source/test/intltest/numbertest.h
index 7f5bcdf..adccc9e 100644
--- a/icu4c/source/test/intltest/numbertest.h
+++ b/icu4c/source/test/intltest/numbertest.h
@@ -68,6 +68,7 @@
// TODO: Add this method if currency symbols override support is added.
//void symbolsOverride();
void sign();
+ void signNearZero();
void signCoverage();
void decimal();
void scale();
diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp
index 726e7c5..8d82acd 100644
--- a/icu4c/source/test/intltest/numbertest_api.cpp
+++ b/icu4c/source/test/intltest/numbertest_api.cpp
@@ -86,6 +86,7 @@
// TODO: Add this method if currency symbols override support is added.
//TESTCASE_AUTO(symbolsOverride);
TESTCASE_AUTO(sign);
+ TESTCASE_AUTO(signNearZero);
TESTCASE_AUTO(signCoverage);
TESTCASE_AUTO(decimal);
TESTCASE_AUTO(scale);
@@ -1694,6 +1695,57 @@
u"00.008765",
u"00");
+ assertFormatDescending(
+ u"Integer Width Compact",
+ u"compact-short integer-width/000",
+ NumberFormatter::with()
+ .notation(Notation::compactShort())
+ .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
+ Locale::getEnglish(),
+ u"088K",
+ u"008.8K",
+ u"876",
+ u"088",
+ u"008.8",
+ u"000.88",
+ u"000.088",
+ u"000.0088",
+ u"000");
+
+ assertFormatDescending(
+ u"Integer Width Scientific",
+ u"scientific integer-width/000",
+ NumberFormatter::with()
+ .notation(Notation::scientific())
+ .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
+ Locale::getEnglish(),
+ u"008.765E4",
+ u"008.765E3",
+ u"008.765E2",
+ u"008.765E1",
+ u"008.765E0",
+ u"008.765E-1",
+ u"008.765E-2",
+ u"008.765E-3",
+ u"000E0");
+
+ assertFormatDescending(
+ u"Integer Width Engineering",
+ u"engineering integer-width/000",
+ NumberFormatter::with()
+ .notation(Notation::engineering())
+ .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
+ Locale::getEnglish(),
+ u"087.65E3",
+ u"008.765E3",
+ u"876.5E0",
+ u"087.65E0",
+ u"008.765E0",
+ u"876.5E-3",
+ u"087.65E-3",
+ u"008.765E-3",
+ u"000E0");
+
assertFormatSingle(
u"Integer Width Remove All A",
u"integer-width/00",
@@ -2104,6 +2156,49 @@
u"-444,444.00 US dollars");
}
+void NumberFormatterApiTest::signNearZero() {
+ // https://unicode-org.atlassian.net/browse/ICU-20709
+ IcuTestErrorCode status(*this, "signNearZero");
+ const struct TestCase {
+ UNumberSignDisplay sign;
+ double input;
+ const char16_t* expected;
+ } cases[] = {
+ { UNUM_SIGN_AUTO, 1.1, u"1" },
+ { UNUM_SIGN_AUTO, 0.9, u"1" },
+ { UNUM_SIGN_AUTO, 0.1, u"0" },
+ { UNUM_SIGN_AUTO, -0.1, u"-0" }, // interesting case
+ { UNUM_SIGN_AUTO, -0.9, u"-1" },
+ { UNUM_SIGN_AUTO, -1.1, u"-1" },
+ { UNUM_SIGN_ALWAYS, 1.1, u"+1" },
+ { UNUM_SIGN_ALWAYS, 0.9, u"+1" },
+ { UNUM_SIGN_ALWAYS, 0.1, u"+0" },
+ { UNUM_SIGN_ALWAYS, -0.1, u"-0" },
+ { UNUM_SIGN_ALWAYS, -0.9, u"-1" },
+ { UNUM_SIGN_ALWAYS, -1.1, u"-1" },
+ { UNUM_SIGN_EXCEPT_ZERO, 1.1, u"+1" },
+ { UNUM_SIGN_EXCEPT_ZERO, 0.9, u"+1" },
+ { UNUM_SIGN_EXCEPT_ZERO, 0.1, u"0" }, // interesting case
+ { UNUM_SIGN_EXCEPT_ZERO, -0.1, u"0" }, // interesting case
+ { UNUM_SIGN_EXCEPT_ZERO, -0.9, u"-1" },
+ { UNUM_SIGN_EXCEPT_ZERO, -1.1, u"-1" },
+ };
+ for (auto& cas : cases) {
+ auto sign = cas.sign;
+ auto input = cas.input;
+ auto expected = cas.expected;
+ auto actual = NumberFormatter::with()
+ .sign(sign)
+ .precision(Precision::integer())
+ .locale(Locale::getUS())
+ .formatDouble(input, status)
+ .toString(status);
+ assertEquals(
+ DoubleToUnicodeString(input) + " @ SignDisplay " + Int64ToUnicodeString(sign),
+ expected, actual);
+ }
+}
+
void NumberFormatterApiTest::signCoverage() {
// https://unicode-org.atlassian.net/browse/ICU-20708
IcuTestErrorCode status(*this, "signCoverage");
diff --git a/icu4c/source/test/testdata/numberpermutationtest.txt b/icu4c/source/test/testdata/numberpermutationtest.txt
index b9c8558..4b4656e 100644
--- a/icu4c/source/test/testdata/numberpermutationtest.txt
+++ b/icu4c/source/test/testdata/numberpermutationtest.txt
@@ -2273,15 +2273,15 @@
es-MX
0
+92 k
- -0
+ 0
zh-TW
0
+9萬
- -0
+ 0
bn-BD
০
+৯২ হা
- -০
+ ০
compact-short .000 sign-accounting-except-zero
es-MX
@@ -4849,15 +4849,15 @@
es-MX
0 %
+91,827 %
- -0 %
+ 0 %
zh-TW
0%
+91,827%
- -0%
+ 0%
bn-BD
০%
+৯১,৮২৭%
- -০%
+ ০%
percent .000 sign-accounting-except-zero
es-MX
@@ -4905,15 +4905,15 @@
es-MX
EUR 0
+EUR 91,827
- -EUR 0
+ EUR 0
zh-TW
€0
+€91,827
- (€0)
+ €0
bn-BD
০€
+৯১,৮২৭€
- (০ €)
+ ০€
currency/EUR .000 sign-accounting-except-zero
es-MX
@@ -4961,15 +4961,15 @@
es-MX
0 fur
+91,827 fur
- -0 fur
+ 0 fur
zh-TW
0 化朗
+91,827 化朗
- -0 化朗
+ 0 化朗
bn-BD
০ ফার্লং
+৯১,৮২৭ ফার্লং
- -০ ফার্লং
+ ০ ফার্লং
measure-unit/length-furlong .000 sign-accounting-except-zero
es-MX
@@ -6627,15 +6627,15 @@
es-MX
0
+91,827
- -0
+ 0
zh-TW
0
+91,827
- -0
+ 0
bn-BD
০
+৯১,৮২৭
- -০
+ ০
unit-width-narrow .000 sign-accounting-except-zero
es-MX
@@ -6683,15 +6683,15 @@
es-MX
0
+91,827
- -0
+ 0
zh-TW
0
+91,827
- -0
+ 0
bn-BD
০
+৯১,৮২৭
- -০
+ ০
unit-width-full-name .000 sign-accounting-except-zero
es-MX
@@ -7943,15 +7943,15 @@
es-MX
00
+1827
- -00
+ 00
zh-TW
00
+1,827
- -00
+ 00
bn-BD
০০
+১,৮২৭
- -০০
+ ০০
.000 integer-width/##00 sign-accounting-except-zero
es-MX
@@ -8167,15 +8167,15 @@
es-MX
0
+45,914
- -0
+ 0
zh-TW
0
+45,914
- -0
+ 0
bn-BD
০
+৪৫,৯১৪
- -০
+ ০
.000 scale/0.5 sign-accounting-except-zero
es-MX
@@ -8335,15 +8335,15 @@
es-MX
0
+91,827
- -0
+ 0
zh-TW
0
+91,827
- -0
+ 0
bn-BD
০
+৯১,৮২৭
- -০
+ ০
.000 group-on-aligned sign-accounting-except-zero
es-MX
@@ -8447,15 +8447,15 @@
es-MX
0
+91,827
- -0
+ 0
zh-TW
0
+91,827
- -0
+ 0
bn-BD
0
+91,827
- -0
+ 0
.000 latin sign-accounting-except-zero
es-MX
@@ -8559,15 +8559,15 @@
es-MX
0.
+91,827.
- -0.
+ 0.
zh-TW
0.
+91,827.
- -0.
+ 0.
bn-BD
০.
+৯১,৮২৭.
- -০.
+ ০.
.000 sign-accounting-except-zero decimal-always
es-MX
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java
index 8dad5fc..2a44151 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java
@@ -231,6 +231,9 @@
@Override
public MicroProps processQuantity(DecimalQuantity quantity) {
MicroProps micros = parent.processQuantity(quantity);
+ if (micros.rounder != null) {
+ micros.rounder.apply(quantity);
+ }
if (micros.modMiddle != null) {
return micros;
}
@@ -268,6 +271,9 @@
@Override
public MicroProps processQuantity(DecimalQuantity fq) {
MicroProps micros = parent.processQuantity(fq);
+ if (micros.rounder != null) {
+ micros.rounder.apply(fq);
+ }
if (micros.modMiddle != null) {
return micros;
}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java
index 497800e..ff0a0a0 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatter.java
@@ -327,7 +327,7 @@
/**
* Show the minus sign on negative numbers and the plus sign on positive numbers. Do not show a
- * sign on zero or NaN, unless the sign bit is set (-0.0 gets a sign).
+ * sign on zero, numbers that round to zero, or NaN.
*
* @stable ICU 61
* @see NumberFormatter
@@ -336,9 +336,8 @@
/**
* Use the locale-dependent accounting format on negative numbers, and show the plus sign on
- * positive numbers. Do not show a sign on zero or NaN, unless the sign bit is set (-0.0 gets a
- * sign). For more information on the accounting format, see the ACCOUNTING sign display
- * strategy.
+ * positive numbers. Do not show a sign on zero, numbers that round to zero, or NaN. For more
+ * information on the accounting format, see the ACCOUNTING sign display strategy.
*
* @stable ICU 61
* @see NumberFormatter
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
index 208c02a..48607c0 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
@@ -98,9 +98,6 @@
*/
public MicroProps preProcess(DecimalQuantity inValue) {
MicroProps micros = microPropsGenerator.processQuantity(inValue);
- if (micros.rounder != null) {
- micros.rounder.apply(inValue);
- }
if (micros.integerWidth.maxInt == -1) {
inValue.setMinInteger(micros.integerWidth.minInt);
} else {
@@ -114,9 +111,6 @@
MicroProps micros = new MicroProps(false);
MicroPropsGenerator microPropsGenerator = macrosToMicroGenerator(macros, micros, false);
micros = microPropsGenerator.processQuantity(inValue);
- if (micros.rounder != null) {
- micros.rounder.apply(inValue);
- }
if (micros.integerWidth.maxInt == -1) {
inValue.setMinInteger(micros.integerWidth.minInt);
} else {
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberpermutationtest.txt b/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberpermutationtest.txt
index 25f7da8..4b4656e 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberpermutationtest.txt
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberpermutationtest.txt
@@ -1,4 +1,4 @@
-# © 2017 and later: Unicode, Inc. and others.
+# © 2019 and later: Unicode, Inc. and others.
# License & terms of use: http://www.unicode.org/copyright.html
compact-short percent unit-width-narrow
@@ -2273,15 +2273,15 @@
es-MX
0
+92 k
- -0
+ 0
zh-TW
0
+9萬
- -0
+ 0
bn-BD
০
+৯২ হা
- -০
+ ০
compact-short .000 sign-accounting-except-zero
es-MX
@@ -4849,15 +4849,15 @@
es-MX
0 %
+91,827 %
- -0 %
+ 0 %
zh-TW
0%
+91,827%
- -0%
+ 0%
bn-BD
০%
+৯১,৮২৭%
- -০%
+ ০%
percent .000 sign-accounting-except-zero
es-MX
@@ -4905,15 +4905,15 @@
es-MX
EUR 0
+EUR 91,827
- -EUR 0
+ EUR 0
zh-TW
€0
+€91,827
- (€0)
+ €0
bn-BD
০€
+৯১,৮২৭€
- (০ €)
+ ০€
currency/EUR .000 sign-accounting-except-zero
es-MX
@@ -4961,15 +4961,15 @@
es-MX
0 fur
+91,827 fur
- -0 fur
+ 0 fur
zh-TW
0 化朗
+91,827 化朗
- -0 化朗
+ 0 化朗
bn-BD
০ ফার্লং
+৯১,৮২৭ ফার্লং
- -০ ফার্লং
+ ০ ফার্লং
measure-unit/length-furlong .000 sign-accounting-except-zero
es-MX
@@ -6627,15 +6627,15 @@
es-MX
0
+91,827
- -0
+ 0
zh-TW
0
+91,827
- -0
+ 0
bn-BD
০
+৯১,৮২৭
- -০
+ ০
unit-width-narrow .000 sign-accounting-except-zero
es-MX
@@ -6683,15 +6683,15 @@
es-MX
0
+91,827
- -0
+ 0
zh-TW
0
+91,827
- -0
+ 0
bn-BD
০
+৯১,৮২৭
- -০
+ ০
unit-width-full-name .000 sign-accounting-except-zero
es-MX
@@ -7943,15 +7943,15 @@
es-MX
00
+1827
- -00
+ 00
zh-TW
00
+1,827
- -00
+ 00
bn-BD
০০
+১,৮২৭
- -০০
+ ০০
.000 integer-width/##00 sign-accounting-except-zero
es-MX
@@ -8167,15 +8167,15 @@
es-MX
0
+45,914
- -0
+ 0
zh-TW
0
+45,914
- -0
+ 0
bn-BD
০
+৪৫,৯১৪
- -০
+ ০
.000 scale/0.5 sign-accounting-except-zero
es-MX
@@ -8335,15 +8335,15 @@
es-MX
0
+91,827
- -0
+ 0
zh-TW
0
+91,827
- -0
+ 0
bn-BD
০
+৯১,৮২৭
- -০
+ ০
.000 group-on-aligned sign-accounting-except-zero
es-MX
@@ -8447,15 +8447,15 @@
es-MX
0
+91,827
- -0
+ 0
zh-TW
0
+91,827
- -0
+ 0
bn-BD
0
+91,827
- -0
+ 0
.000 latin sign-accounting-except-zero
es-MX
@@ -8559,15 +8559,15 @@
es-MX
0.
+91,827.
- -0.
+ 0.
zh-TW
0.
+91,827.
- -0.
+ 0.
bn-BD
০.
+৯১,৮২৭.
- -০.
+ ০.
.000 sign-accounting-except-zero decimal-always
es-MX
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
index dbbf464..a12468f 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
@@ -1625,6 +1625,57 @@
"00.008765",
"00");
+ assertFormatDescending(
+ "Integer Width Compact",
+ "compact-short integer-width/000",
+ NumberFormatter.with()
+ .notation(Notation.compactShort())
+ .integerWidth(IntegerWidth.zeroFillTo(3).truncateAt(3)),
+ ULocale.ENGLISH,
+ "088K",
+ "008.8K",
+ "876",
+ "088",
+ "008.8",
+ "000.88",
+ "000.088",
+ "000.0088",
+ "000");
+
+ assertFormatDescending(
+ "Integer Width Scientific",
+ "scientific integer-width/000",
+ NumberFormatter.with()
+ .notation(Notation.scientific())
+ .integerWidth(IntegerWidth.zeroFillTo(3).truncateAt(3)),
+ ULocale.ENGLISH,
+ "008.765E4",
+ "008.765E3",
+ "008.765E2",
+ "008.765E1",
+ "008.765E0",
+ "008.765E-1",
+ "008.765E-2",
+ "008.765E-3",
+ "000E0");
+
+ assertFormatDescending(
+ "Integer Width Engineering",
+ "engineering integer-width/000",
+ NumberFormatter.with()
+ .notation(Notation.engineering())
+ .integerWidth(IntegerWidth.zeroFillTo(3).truncateAt(3)),
+ ULocale.ENGLISH,
+ "087.65E3",
+ "008.765E3",
+ "876.5E0",
+ "087.65E0",
+ "008.765E0",
+ "876.5E-3",
+ "087.65E-3",
+ "008.765E-3",
+ "000E0");
+
assertFormatSingle(
"Integer Width Remove All A",
"integer-width/00",
@@ -2030,6 +2081,45 @@
}
@Test
+ public void signNearZero() {
+ // https://unicode-org.atlassian.net/browse/ICU-20709
+ Object[][] cases = {
+ { SignDisplay.AUTO, 1.1, "1" },
+ { SignDisplay.AUTO, 0.9, "1" },
+ { SignDisplay.AUTO, 0.1, "0" },
+ { SignDisplay.AUTO, -0.1, "-0" }, // interesting case
+ { SignDisplay.AUTO, -0.9, "-1" },
+ { SignDisplay.AUTO, -1.1, "-1" },
+ { SignDisplay.ALWAYS, 1.1, "+1" },
+ { SignDisplay.ALWAYS, 0.9, "+1" },
+ { SignDisplay.ALWAYS, 0.1, "+0" },
+ { SignDisplay.ALWAYS, -0.1, "-0" },
+ { SignDisplay.ALWAYS, -0.9, "-1" },
+ { SignDisplay.ALWAYS, -1.1, "-1" },
+ { SignDisplay.EXCEPT_ZERO, 1.1, "+1" },
+ { SignDisplay.EXCEPT_ZERO, 0.9, "+1" },
+ { SignDisplay.EXCEPT_ZERO, 0.1, "0" }, // interesting case
+ { SignDisplay.EXCEPT_ZERO, -0.1, "0" }, // interesting case
+ { SignDisplay.EXCEPT_ZERO, -0.9, "-1" },
+ { SignDisplay.EXCEPT_ZERO, -1.1, "-1" },
+ };
+ for (Object[] cas : cases) {
+ SignDisplay sign = (SignDisplay) cas[0];
+ double input = (Double) cas[1];
+ String expected = (String) cas[2];
+ String actual = NumberFormatter.with()
+ .sign(sign)
+ .precision(Precision.integer())
+ .locale(Locale.US)
+ .format(input)
+ .toString();
+ assertEquals(
+ input + " @ SignDisplay " + sign,
+ expected, actual);
+ }
+ }
+
+ @Test
public void signCoverage() {
// https://unicode-org.atlassian.net/browse/ICU-20708
Object[][][] cases = new Object[][][] {
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberPermutationTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberPermutationTest.java
index 3ce326f..d7056b9 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberPermutationTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberPermutationTest.java
@@ -96,7 +96,7 @@
// Build up the golden data string as we evaluate all permutations
ArrayList<String> resultLines = new ArrayList<>();
- resultLines.add("# © 2017 and later: Unicode, Inc. and others.");
+ resultLines.add("# © 2019 and later: Unicode, Inc. and others.");
resultLines.add("# License & terms of use: http://www.unicode.org/copyright.html");
resultLines.add("");