Skip to content

Commit

Permalink
ICU-21109 minimum grouping digits in DecimalFormat
Browse files Browse the repository at this point in the history
See #1152
  • Loading branch information
FrankYFTang committed Jun 11, 2020
1 parent 0735ea8 commit e7bd5b1
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 21 deletions.
14 changes: 7 additions & 7 deletions icu4c/source/i18n/number_grouping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ Grouper Grouper::forProperties(const DecimalFormatProperties& properties) {
}

void Grouper::setLocaleData(const impl::ParsedPatternInfo &patternInfo, const Locale& locale) {
if (fMinGrouping == -2) {
fMinGrouping = getMinGroupingForLocale(locale);
} else if (fMinGrouping == -3) {
fMinGrouping = static_cast<int16_t>(uprv_max(2, getMinGroupingForLocale(locale)));
} else {
// leave fMinGrouping alone
}
if (fGrouping1 != -2 && fGrouping2 != -4) {
return;
}
Expand All @@ -76,13 +83,6 @@ void Grouper::setLocaleData(const impl::ParsedPatternInfo &patternInfo, const Lo
if (grouping3 == -1) {
grouping2 = grouping1;
}
if (fMinGrouping == -2) {
fMinGrouping = getMinGroupingForLocale(locale);
} else if (fMinGrouping == -3) {
fMinGrouping = static_cast<int16_t>(uprv_max(2, getMinGroupingForLocale(locale)));
} else {
// leave fMinGrouping alone
}
fGrouping1 = grouping1;
fGrouping2 = grouping2;
}
Expand Down
11 changes: 9 additions & 2 deletions icu4c/source/i18n/unicode/decimfmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1674,8 +1674,15 @@ class U_I18N_API DecimalFormat : public NumberFormat {
int32_t getMinimumGroupingDigits() const;

/**
* Sets the minimum grouping digits. Setting to a value less than or
* equal to 1 turns off minimum grouping digits.
* Sets the minimum grouping digits. Setting the value to
* - 1: Turns off minimum grouping digits.
* - 0 or -1: The behavior is undefined.
* - UNUM_MINIMUM_GROUPING_DIGITS_AUTO: Display grouping using the default
* strategy for all locales.
* - UNUM_MINIMUM_GROUPING_DIGITS_MIN2: Display grouping using locale
* defaults, except do not show grouping on values smaller than 10000
* (such that there is a minimum of two digits before the first
* separator).
*
* For more control over grouping strategies, use NumberFormatter.
*
Expand Down
24 changes: 24 additions & 0 deletions icu4c/source/i18n/unicode/unum.h
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,30 @@ typedef enum UNumberFormatFields {
} UNumberFormatFields;


#ifndef U_HIDE_DRAFT_API
/**
* Selectors with special numeric values to use locale default minimum grouping
* digits for the DecimalFormat/UNumberFormat setMinimumGroupingDigits method.
* Do not use these constants with the [U]NumberFormatter API.
*
* @draft ICU 68
*/
typedef enum UNumberFormatMinimumGroupingDigits {
/**
* Display grouping using the default strategy for all locales.
* @draft ICU 68
*/
UNUM_MINIMUM_GROUPING_DIGITS_AUTO = -2,
/**
* Display grouping using locale defaults, except do not show grouping on
* values smaller than 10000 (such that there is a minimum of two digits
* before the first separator).
* @draft ICU 68
*/
UNUM_MINIMUM_GROUPING_DIGITS_MIN2 = -3,
} UNumberFormatMinimumGroupingDigits;
#endif // U_HIDE_DRAFT_API

/**
* Create and return a new UNumberFormat for formatting and parsing
* numbers. A UNumberFormat may be used to format numbers by calling
Expand Down
37 changes: 37 additions & 0 deletions icu4c/source/test/intltest/numfmtst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9078,6 +9078,43 @@ void NumberFormatTest::TestMinimumGroupingDigits() {
df.format(12345, result.remove(), status);
status.errIfFailureAndReset();
assertEquals("Should have grouping", u"12,345", result);


// Test special values -1, UNUM_MINIMUM_GROUPING_DIGITS_AUTO and
// UNUM_MINIMUM_GROUPING_DIGITS_MIN2
struct TestCase {
const char* locale;
int32_t minGroup;
double input;
const char16_t* expected;
} cases[] = {
{ "en-US", 1, 1000, u"1,000" },
{ "en-US", 1, 10000, u"10,000" },
{ "en-US", UNUM_MINIMUM_GROUPING_DIGITS_AUTO, 1000, u"1,000" },
{ "en-US", UNUM_MINIMUM_GROUPING_DIGITS_AUTO, 10000, u"10,000" },
{ "en-US", UNUM_MINIMUM_GROUPING_DIGITS_MIN2, 1000, u"1000" },
{ "en-US", UNUM_MINIMUM_GROUPING_DIGITS_MIN2, 10000, u"10,000" },

{ "es", 1, 1000, u"1.000" },
{ "es", 1, 10000, u"10.000" },
{ "es", UNUM_MINIMUM_GROUPING_DIGITS_AUTO, 1000, u"1000" },
{ "es", UNUM_MINIMUM_GROUPING_DIGITS_AUTO, 10000, u"10.000" },
{ "es", UNUM_MINIMUM_GROUPING_DIGITS_MIN2, 1000, u"1000" },
{ "es", UNUM_MINIMUM_GROUPING_DIGITS_MIN2, 10000, u"10.000" },
};
for (const auto& cas : cases) {
UnicodeString message = UnicodeString(cas.locale)
+ u" " + Int64ToUnicodeString(cas.minGroup)
+ u" " + DoubleToUnicodeString(cas.input);
status.setScope(message);
DecimalFormat df(u"#,##0", {cas.locale, status}, status);
if (status.errIfFailureAndReset()) { continue; }
df.setMinimumGroupingDigits(cas.minGroup);
UnicodeString actual;
df.format(cas.input, actual, status);
if (status.errIfFailureAndReset()) { continue; }
assertEquals(message, cas.expected, actual);
}
}

void NumberFormatTest::Test11897_LocalizedPatternSeparator() {
Expand Down
27 changes: 15 additions & 12 deletions icu4j/main/classes/core/src/com/ibm/icu/impl/number/Grouper.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,20 @@ private Grouper(short grouping1, short grouping2, short minGrouping) {
}

public Grouper withLocaleData(ULocale locale, ParsedPatternInfo patternInfo) {
if (this.grouping1 != -2 && this.grouping1 != -4) {
return this;
short minGrouping;
if (this.minGrouping == -2) {
minGrouping = getMinGroupingForLocale(locale);
} else if (this.minGrouping == -3) {
minGrouping = (short) Math.max(2, getMinGroupingForLocale(locale));
} else {
minGrouping = this.minGrouping;
}

if (this.grouping1 != -2 && this.grouping2 != -4) {
if (minGrouping == this.minGrouping) {
return this;
}
return getInstance(this.grouping1, this.grouping2, minGrouping);
}

short grouping1 = (short) (patternInfo.positive.groupingSizes & 0xffff);
Expand All @@ -133,15 +145,6 @@ public Grouper withLocaleData(ULocale locale, ParsedPatternInfo patternInfo) {
grouping2 = grouping1;
}

short minGrouping;
if (this.minGrouping == -2) {
minGrouping = getMinGroupingForLocale(locale);
} else if (this.minGrouping == -3) {
minGrouping = (short) Math.max(2, getMinGroupingForLocale(locale));
} else {
minGrouping = this.minGrouping;
}

return getInstance(grouping1, grouping2, minGrouping);
}

Expand All @@ -164,4 +167,4 @@ public short getPrimary() {
public short getSecondary() {
return grouping2;
}
}
}
35 changes: 35 additions & 0 deletions icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,16 @@ public synchronized int getMinimumGroupingDigits() {
* {@icu} Sets the minimum number of digits that must be before the first grouping separator in
* order for the grouping separator to be printed. For example, if minimum grouping digits is set
* to 2, in <em>en-US</em>, 1234 will be printed as "1234" and 12345 will be printed as "12,345".
*
* Set the value to:
* <ul>
* <li>1 to turn off minimum grouping digits.</li>
* <li>MINIMUM_GROUPING_DIGITS_AUTO to display grouping using the default
* strategy for all locales.</li>
* <li>MINIMUM_GROUPING_DIGITS_MIN2 to display grouping using locale defaults,
* except do not show grouping on values smaller than 10000 (such that there is a minimum of
* two digits before the first separator).</li>
* </ul>
*
* @param number The minimum number of digits before grouping is triggered.
* @category Separators
Expand All @@ -2004,6 +2014,31 @@ public synchronized void setMinimumGroupingDigits(int number) {
refreshFormatter();
}

/**
* {@icu} Constant for {@link #setMinimumGroupingDigits()} to specify display
* grouping using the default strategy for all locales.
*
* @see #setMinimumGroupingDigits
* @see #MINIMUM_GROUPING_DIGITS_MIN2
* @category Separators
* @provisional This API might change or be removed in a future release.
* @draft ICU 68
*/
public static final int MINIMUM_GROUPING_DIGITS_AUTO = -2;

/**
* {@icu} Constant for {@link #setMinimumGroupingDigits()} to specify display
* grouping using locale defaults, except do not show grouping on values smaller than
* 10000 (such that there is a minimum of two digits before the first separator).
*
* @see #setMinimumGroupingDigits
* @see #MINIMUM_GROUPING_DIGITS_AUTO
* @category Separators
* @provisional This API might change or be removed in a future release.
* @draft ICU 68
*/
public static final int MINIMUM_GROUPING_DIGITS_MIN2 = -3;

/**
* Returns whether the decimal separator is shown on integers.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6136,6 +6136,41 @@ public void testMinimumGroupingDigits() {
actual = df.format(l);
assertEquals("Output is wrong for 2, "+i, allExpected[i][1], actual);
}

String[] locales = {"en-US", "es"};
int[] groupingDigits = {
1,
DecimalFormat.MINIMUM_GROUPING_DIGITS_AUTO,
DecimalFormat.MINIMUM_GROUPING_DIGITS_MIN2
};
int[] values = {1000, 10000};
String[] allExpected2 = {
// locale: en-US
"1,000", "10,000", // minimumGroupingDigits = 1
"1,000", "10,000", // minimumGroupingDigits = MINIMUM_GROUPING_DIGITS_AUTO
"1000" , "10,000", // minimumGroupingDigits = MINIMUM_GROUPING_DIGITS_MIN2
// locale: es
"1.000", "10.000", // minimumGroupingDigits = 1
"1000", "10.000", // minimumGroupingDigits = MINIMUM_GROUPING_DIGITS_AUTO
"1000", "10.000" // minimumGroupingDigits = MINIMUM_GROUPING_DIGITS_MIN2
};

int i = 0;
for (String locale : locales) {
for (int minimumGroupingDigits : groupingDigits) {
for (int value : values) {
NumberFormat f = NumberFormat.getInstance(new ULocale(locale));
df = (DecimalFormat) f;
df.setMinimumGroupingDigits(minimumGroupingDigits);
String actual = df.format(value);
String expected = allExpected2[i++];
assertEquals("Output is wrong for " + value +
" locale=" + locale + " minimumGroupingDigits=" + minimumGroupingDigits,
expected, actual);
}
}
}

}

@Test
Expand Down

0 comments on commit e7bd5b1

Please sign in to comment.