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.
This commit is contained in:
Shane Carr 2019-09-27 16:33:43 -07:00 committed by Shane F. Carr
parent e7b540d1af
commit 00946cef43
12 changed files with 268 additions and 84 deletions

View File

@ -111,7 +111,6 @@ void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& micro
return;
}
fMicroPropsGenerator->processQuantity(inValue, microsOut, status);
microsOut.rounder.apply(inValue, status);
microsOut.integerWidth.apply(inValue, status);
}
@ -124,7 +123,6 @@ MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErr
return fMicros; // must always return a value
}
fMicroPropsGenerator->processQuantity(inValue, fMicros, status);
fMicros.rounder.apply(inValue, status);
fMicros.integerWidth.apply(inValue, status);
return fMicros;
}

View File

@ -124,6 +124,7 @@ ImmutablePatternModifier::ImmutablePatternModifier(AdoptingModifierStore* pm, co
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 @@ MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerato
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;
}

View File

@ -336,7 +336,7 @@ typedef enum UNumberSignDisplay {
/**
* 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 @@ typedef enum UNumberSignDisplay {
/**
* 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
*/

View File

@ -68,6 +68,7 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
// TODO: Add this method if currency symbols override support is added.
//void symbolsOverride();
void sign();
void signNearZero();
void signCoverage();
void decimal();
void scale();

View File

@ -86,6 +86,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
// 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 @@ void NumberFormatterApiTest::integerWidth() {
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 @@ void NumberFormatterApiTest::sign() {
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");

View File

@ -2273,15 +2273,15 @@ compact-short precision-integer sign-accounting-except-zero
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 @@ percent precision-integer sign-accounting-except-zero
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 @@ currency/EUR precision-integer sign-accounting-except-zero
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 @@ measure-unit/length-furlong precision-integer sign-accounting-except-zero
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 @@ unit-width-narrow precision-integer sign-accounting-except-zero
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 @@ unit-width-full-name precision-integer sign-accounting-except-zero
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 @@ precision-integer integer-width/##00 sign-accounting-except-zero
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 @@ precision-integer scale/0.5 sign-accounting-except-zero
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 @@ precision-integer group-on-aligned sign-accounting-except-zero
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 @@ precision-integer latin sign-accounting-except-zero
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 @@ precision-integer sign-accounting-except-zero decimal-always
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

View File

@ -231,6 +231,9 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr
@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 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr
@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;
}

View File

@ -327,7 +327,7 @@ public final class NumberFormatter {
/**
* 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 @@ public final class NumberFormatter {
/**
* 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

View File

@ -98,9 +98,6 @@ class NumberFormatterImpl {
*/
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 @@ class NumberFormatterImpl {
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 {

View File

@ -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 @@ compact-short precision-integer sign-accounting-except-zero
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 @@ percent precision-integer sign-accounting-except-zero
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 @@ currency/EUR precision-integer sign-accounting-except-zero
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 @@ measure-unit/length-furlong precision-integer sign-accounting-except-zero
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 @@ unit-width-narrow precision-integer sign-accounting-except-zero
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 @@ unit-width-full-name precision-integer sign-accounting-except-zero
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 @@ precision-integer integer-width/##00 sign-accounting-except-zero
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 @@ precision-integer scale/0.5 sign-accounting-except-zero
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 @@ precision-integer group-on-aligned sign-accounting-except-zero
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 @@ precision-integer latin sign-accounting-except-zero
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 @@ precision-integer sign-accounting-except-zero decimal-always
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

View File

@ -1625,6 +1625,57 @@ public class NumberFormatterApiTest {
"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",
@ -2029,6 +2080,45 @@ public class NumberFormatterApiTest {
"-444,444.00 US dollars");
}
@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

View File

@ -96,7 +96,7 @@ public class NumberPermutationTest extends TestFmwk {
// 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("");