ICU-21654 Fix skeleton output for rounding increment in C++

See #1813
This commit is contained in:
Shane F. Carr 2021-09-11 06:48:58 +00:00
parent ff8516b0dd
commit cd3721697a
5 changed files with 169 additions and 8 deletions

View file

@ -1397,18 +1397,14 @@ void blueprint_helpers::parseIncrementOption(const StringSegment &segment, Macro
number::impl::parseIncrementOption(segment, macros.precision, status);
}
void blueprint_helpers::generateIncrementOption(double increment, int32_t trailingZeros, UnicodeString& sb,
void blueprint_helpers::generateIncrementOption(double increment, int32_t minFrac, UnicodeString& sb,
UErrorCode&) {
// Utilize DecimalQuantity/double_conversion to format this for us.
DecimalQuantity dq;
dq.setToDouble(increment);
dq.roundToInfinity();
dq.setMinFraction(minFrac);
sb.append(dq.toPlainString());
// We might need to append extra trailing zeros for min fraction...
if (trailingZeros > 0) {
appendMultiple(sb, u'0', trailingZeros);
}
}
void blueprint_helpers::parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros,
@ -1632,7 +1628,7 @@ bool GeneratorHelpers::precision(const MacroProps& macros, UnicodeString& sb, UE
sb.append(u"precision-increment/", -1);
blueprint_helpers::generateIncrementOption(
impl.fIncrement,
impl.fMinFrac - impl.fMaxFrac,
impl.fMinFrac,
sb,
status);
} else if (macros.precision.fType == Precision::RND_CURRENCY) {

View file

@ -286,7 +286,7 @@ bool parseTrailingZeroOption(const StringSegment& segment, MacroProps& macros, U
void parseIncrementOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void
generateIncrementOption(double increment, int32_t trailingZeros, UnicodeString& sb, UErrorCode& status);
generateIncrementOption(double increment, int32_t minFrac, UnicodeString& sb, UErrorCode& status);
void parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);

View file

@ -73,6 +73,7 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
void roundingFigures();
void roundingFractionFigures();
void roundingOther();
void roundingIncrementSkeleton();
void grouping();
void padding();
void integerWidth();

View file

@ -97,6 +97,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
TESTCASE_AUTO(roundingFigures);
TESTCASE_AUTO(roundingFractionFigures);
TESTCASE_AUTO(roundingOther);
TESTCASE_AUTO(roundingIncrementSkeleton);
TESTCASE_AUTO(grouping);
TESTCASE_AUTO(padding);
TESTCASE_AUTO(integerWidth);
@ -3196,6 +3197,38 @@ void NumberFormatterApiTest::roundingOther() {
u"0.010",
u"0.000");
assertFormatDescending(
u"Integer increment with trailing zeros (ICU-21654)",
u"precision-increment/50",
u"precision-increment/50",
NumberFormatter::with().precision(Precision::increment(50)),
Locale::getEnglish(),
u"87,650",
u"8,750",
u"900",
u"100",
u"0",
u"0",
u"0",
u"0",
u"0");
assertFormatDescending(
u"Integer increment with minFraction (ICU-21654)",
u"precision-increment/5.0",
u"precision-increment/5.0",
NumberFormatter::with().precision(Precision::increment(5).withMinFraction(1)),
Locale::getEnglish(),
u"87,650.0",
u"8,765.0",
u"875.0",
u"90.0",
u"10.0",
u"0.0",
u"0.0",
u"0.0",
u"0.0");
assertFormatDescending(
u"Currency Standard",
u"currency/CZK precision-currency-standard",
@ -3325,6 +3358,55 @@ void NumberFormatterApiTest::roundingOther() {
u"5E-324");
}
/** Test for ICU-21654 */
void NumberFormatterApiTest::roundingIncrementSkeleton() {
IcuTestErrorCode status(*this, "roundingIncrementSkeleton");
Locale locale = Locale::getEnglish();
for (int min_fraction_digits = 1; min_fraction_digits < 8; min_fraction_digits++) {
// pattern is a snprintf pattern string like "precision-increment/%0.5f"
char pattern[256];
snprintf(pattern, 256, "precision-increment/%%0.%df", min_fraction_digits);
double increment = 0.05;
for (int i = 0; i < 8 ; i++, increment *= 10.0) {
const UnlocalizedNumberFormatter f =
NumberFormatter::with().precision(
Precision::increment(increment).withMinFraction(
min_fraction_digits));
const LocalizedNumberFormatter l = f.locale(locale);
std::string skeleton;
f.toSkeleton(status).toUTF8String<std::string>(skeleton);
char message[256];
snprintf(message, 256,
"Precision::increment(%0.5f).withMinFraction(%d) '%s'\n",
increment, min_fraction_digits,
skeleton.c_str());
if (increment == 0.05 && min_fraction_digits == 1) {
// Special case when the number of fraction digits is too low:
// Precision::increment(0.05000).withMinFraction(1) 'precision-increment/0.05'
assertEquals(message, "precision-increment/0.05", skeleton.c_str());
} else {
// All other cases: use the snprintf pattern computed above:
// Precision::increment(0.50000).withMinFraction(1) 'precision-increment/0.5'
// Precision::increment(5.00000).withMinFraction(1) 'precision-increment/5.0'
// Precision::increment(50.00000).withMinFraction(1) 'precision-increment/50.0'
// ...
// Precision::increment(0.05000).withMinFraction(2) 'precision-increment/0.05'
// Precision::increment(0.50000).withMinFraction(2) 'precision-increment/0.50'
// Precision::increment(5.00000).withMinFraction(2) 'precision-increment/5.00'
// ...
char expected[256];
snprintf(expected, 256, pattern, increment);
assertEquals(message, expected, skeleton.c_str());
}
}
}
}
void NumberFormatterApiTest::grouping() {
assertFormatDescendingBig(
u"Western Grouping",

View file

@ -3182,6 +3182,38 @@ public class NumberFormatterApiTest extends TestFmwk {
"0.010",
"0.000");
assertFormatDescending(
"Integer increment with trailing zeros (ICU-21654)",
"precision-increment/50",
"precision-increment/50",
NumberFormatter.with().precision(Precision.increment(new BigDecimal("50"))),
ULocale.ENGLISH,
"87,650",
"8,750",
"900",
"100",
"0",
"0",
"0",
"0",
"0");
assertFormatDescending(
"Integer increment with minFraction (ICU-21654)",
"precision-increment/5.0",
"precision-increment/5.0",
NumberFormatter.with().precision(Precision.increment(new BigDecimal("5.0"))),
ULocale.ENGLISH,
"87,650.0",
"8,765.0",
"875.0",
"90.0",
"10.0",
"0.0",
"0.0",
"0.0",
"0.0");
assertFormatDescending(
"Currency Standard",
"currency/CZK precision-currency-standard",
@ -3301,6 +3333,56 @@ public class NumberFormatterApiTest extends TestFmwk {
"4.9E-324");
}
@Test
public void roundingIncrementSkeleton() {
ULocale locale = ULocale.ENGLISH;
for (int min_fraction_digits = 1; min_fraction_digits < 8; min_fraction_digits++) {
// pattern is a snprintf pattern string like "precision-increment/%.5f"
String pattern = String.format("precision-increment/%%.%df", min_fraction_digits);
double increment = 0.05;
for (int i = 0; i < 8 ; i++, increment *= 10.0) {
BigDecimal bdIncrement;
if (increment == 0.05 && min_fraction_digits == 1) {
// Special case when the number of fraction digits is too low:
bdIncrement = new BigDecimal("0.05");
} else {
bdIncrement = BigDecimal.valueOf(increment).setScale(min_fraction_digits);
}
UnlocalizedNumberFormatter f =
NumberFormatter.with().precision(
Precision.increment(bdIncrement));
LocalizedNumberFormatter l = f.locale(locale);
String skeleton = f.toSkeleton();
String message = String.format(
"Precision::increment(%.5f).withMinFraction(%d) '%s'\n",
increment, min_fraction_digits,
skeleton);
if (increment == 0.05 && min_fraction_digits == 1) {
// Special case when the number of fraction digits is too low:
// Precision::increment(0.05000).withMinFraction(1) 'precision-increment/0.05'
assertEquals(message, "precision-increment/0.05", skeleton);
} else {
// All other cases: use the snprintf pattern computed above:
// Precision::increment(0.50000).withMinFraction(1) 'precision-increment/0.5'
// Precision::increment(5.00000).withMinFraction(1) 'precision-increment/5.0'
// Precision::increment(50.00000).withMinFraction(1) 'precision-increment/50.0'
// ...
// Precision::increment(0.05000).withMinFraction(2) 'precision-increment/0.05'
// Precision::increment(0.50000).withMinFraction(2) 'precision-increment/0.50'
// Precision::increment(5.00000).withMinFraction(2) 'precision-increment/5.00'
// ...
String expected = String.format(pattern, increment);
assertEquals(message, expected, skeleton);
}
}
}
}
@Test
public void grouping() {
assertFormatDescendingBig(