icu/source/test/intltest/numbertest_api.cpp

1510 lines
48 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
#include "charstr.h"
#include <cstdarg>
#include "unicode/unum.h"
#include "unicode/numberformatter.h"
#include "number_types.h"
#include "numbertest.h"
// Horrible workaround for the lack of a status code in the constructor...
UErrorCode globalNumberFormatterApiTestStatus = U_ZERO_ERROR;
NumberFormatterApiTest::NumberFormatterApiTest()
: NumberFormatterApiTest(globalNumberFormatterApiTestStatus) {
}
NumberFormatterApiTest::NumberFormatterApiTest(UErrorCode &status)
: USD(u"USD", status), GBP(u"GBP", status),
CZK(u"CZK", status), CAD(u"CAD", status),
FRENCH_SYMBOLS(Locale::getFrench(), status),
SWISS_SYMBOLS(Locale("de-CH"), status),
MYANMAR_SYMBOLS(Locale("my"), status) {
MeasureUnit *unit = MeasureUnit::createMeter(status);
if (U_FAILURE(status)) {
dataerrln("%s %d status = %s", __FILE__, __LINE__, u_errorName(status));
return;
}
METER = *unit;
delete unit;
unit = MeasureUnit::createDay(status);
DAY = *unit;
delete unit;
unit = MeasureUnit::createSquareMeter(status);
SQUARE_METER = *unit;
delete unit;
unit = MeasureUnit::createFahrenheit(status);
FAHRENHEIT = *unit;
delete unit;
NumberingSystem *ns = NumberingSystem::createInstanceByName("mathsanb", status);
MATHSANB = *ns;
delete ns;
ns = NumberingSystem::createInstanceByName("latn", status);
LATN = *ns;
delete ns;
}
void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) {
if (exec) {
logln("TestSuite NumberFormatterApiTest: ");
}
TESTCASE_AUTO_BEGIN;
TESTCASE_AUTO(notationSimple);
TESTCASE_AUTO(notationScientific);
TESTCASE_AUTO(notationCompact);
TESTCASE_AUTO(unitMeasure);
TESTCASE_AUTO(unitCurrency);
TESTCASE_AUTO(unitPercent);
TESTCASE_AUTO(roundingFraction);
TESTCASE_AUTO(roundingFigures);
TESTCASE_AUTO(roundingFractionFigures);
TESTCASE_AUTO(roundingOther);
TESTCASE_AUTO(grouping);
TESTCASE_AUTO(padding);
TESTCASE_AUTO(integerWidth);
TESTCASE_AUTO(symbols);
// TODO: Add this method if currency symbols override support is added.
//TESTCASE_AUTO(symbolsOverride);
TESTCASE_AUTO(sign);
TESTCASE_AUTO(decimal);
TESTCASE_AUTO(locale);
TESTCASE_AUTO(formatTypes);
TESTCASE_AUTO(errors);
TESTCASE_AUTO_END;
}
void NumberFormatterApiTest::notationSimple() {
assertFormatDescending(
u"Basic",
NumberFormatter::with(),
Locale::getEnglish(),
u"87,650",
u"8,765",
u"876.5",
u"87.65",
u"8.765",
u"0.8765",
u"0.08765",
u"0.008765",
u"0");
assertFormatSingle(
u"Basic with Negative Sign",
NumberFormatter::with(),
Locale::getEnglish(),
-9876543.21,
u"-9,876,543.21");
}
void NumberFormatterApiTest::notationScientific() {
assertFormatDescending(
u"Scientific",
NumberFormatter::with().notation(Notation::scientific()),
Locale::getEnglish(),
u"8.765E4",
u"8.765E3",
u"8.765E2",
u"8.765E1",
u"8.765E0",
u"8.765E-1",
u"8.765E-2",
u"8.765E-3",
u"0E0");
assertFormatDescending(
u"Engineering",
NumberFormatter::with().notation(Notation::engineering()),
Locale::getEnglish(),
u"87.65E3",
u"8.765E3",
u"876.5E0",
u"87.65E0",
u"8.765E0",
u"876.5E-3",
u"87.65E-3",
u"8.765E-3",
u"0E0");
assertFormatDescending(
u"Scientific sign always shown",
NumberFormatter::with().notation(
Notation::scientific().withExponentSignDisplay(UNumberSignDisplay::UNUM_SIGN_ALWAYS)),
Locale::getEnglish(),
u"8.765E+4",
u"8.765E+3",
u"8.765E+2",
u"8.765E+1",
u"8.765E+0",
u"8.765E-1",
u"8.765E-2",
u"8.765E-3",
u"0E+0");
assertFormatDescending(
u"Scientific min exponent digits",
NumberFormatter::with().notation(Notation::scientific().withMinExponentDigits(2)),
Locale::getEnglish(),
u"8.765E04",
u"8.765E03",
u"8.765E02",
u"8.765E01",
u"8.765E00",
u"8.765E-01",
u"8.765E-02",
u"8.765E-03",
u"0E00");
assertFormatSingle(
u"Scientific Negative",
NumberFormatter::with().notation(Notation::scientific()),
Locale::getEnglish(),
-1000000,
u"-1E6");
}
void NumberFormatterApiTest::notationCompact() {
assertFormatDescending(
u"Compact Short",
NumberFormatter::with().notation(Notation::compactShort()),
Locale::getEnglish(),
u"88K",
u"8.8K",
u"876",
u"88",
u"8.8",
u"0.88",
u"0.088",
u"0.0088",
u"0");
assertFormatDescending(
u"Compact Long",
NumberFormatter::with().notation(Notation::compactLong()),
Locale::getEnglish(),
u"88 thousand",
u"8.8 thousand",
u"876",
u"88",
u"8.8",
u"0.88",
u"0.088",
u"0.0088",
u"0");
assertFormatDescending(
u"Compact Short Currency",
NumberFormatter::with().notation(Notation::compactShort()).unit(USD),
Locale::getEnglish(),
u"$88K",
u"$8.8K",
u"$876",
u"$88",
u"$8.8",
u"$0.88",
u"$0.088",
u"$0.0088",
u"$0");
assertFormatDescending(
u"Compact Short with ISO Currency",
NumberFormatter::with().notation(Notation::compactShort())
.unit(USD)
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
Locale::getEnglish(),
u"USD 88K",
u"USD 8.8K",
u"USD 876",
u"USD 88",
u"USD 8.8",
u"USD 0.88",
u"USD 0.088",
u"USD 0.0088",
u"USD 0");
assertFormatDescending(
u"Compact Short with Long Name Currency",
NumberFormatter::with().notation(Notation::compactShort())
.unit(USD)
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
Locale::getEnglish(),
u"88K US dollars",
u"8.8K US dollars",
u"876 US dollars",
u"88 US dollars",
u"8.8 US dollars",
u"0.88 US dollars",
u"0.088 US dollars",
u"0.0088 US dollars",
u"0 US dollars");
// Note: Most locales don't have compact long currency, so this currently falls back to short.
// This test case should be fixed when proper compact long currency patterns are added.
assertFormatDescending(
u"Compact Long Currency",
NumberFormatter::with().notation(Notation::compactLong()).unit(USD),
Locale::getEnglish(),
u"$88K", // should be something like "$88 thousand"
u"$8.8K",
u"$876",
u"$88",
u"$8.8",
u"$0.88",
u"$0.088",
u"$0.0088",
u"$0");
// Note: Most locales don't have compact long currency, so this currently falls back to short.
// This test case should be fixed when proper compact long currency patterns are added.
assertFormatDescending(
u"Compact Long with ISO Currency",
NumberFormatter::with().notation(Notation::compactLong())
.unit(USD)
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
Locale::getEnglish(),
u"USD 88K", // should be something like "USD 88 thousand"
u"USD 8.8K",
u"USD 876",
u"USD 88",
u"USD 8.8",
u"USD 0.88",
u"USD 0.088",
u"USD 0.0088",
u"USD 0");
// TODO: This behavior could be improved and should be revisited.
assertFormatDescending(
u"Compact Long with Long Name Currency",
NumberFormatter::with().notation(Notation::compactLong())
.unit(USD)
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
Locale::getEnglish(),
u"88 thousand US dollars",
u"8.8 thousand US dollars",
u"876 US dollars",
u"88 US dollars",
u"8.8 US dollars",
u"0.88 US dollars",
u"0.088 US dollars",
u"0.0088 US dollars",
u"0 US dollars");
assertFormatSingle(
u"Compact Plural One",
NumberFormatter::with().notation(Notation::compactLong()),
Locale::createFromName("es"),
1000000,
u"1 millón");
assertFormatSingle(
u"Compact Plural Other",
NumberFormatter::with().notation(Notation::compactLong()),
Locale::createFromName("es"),
2000000,
u"2 millones");
assertFormatSingle(
u"Compact with Negative Sign",
NumberFormatter::with().notation(Notation::compactShort()),
Locale::getEnglish(),
-9876543.21,
u"-9.9M");
assertFormatSingle(
u"Compact Rounding",
NumberFormatter::with().notation(Notation::compactShort()),
Locale::getEnglish(),
990000,
u"990K");
assertFormatSingle(
u"Compact Rounding",
NumberFormatter::with().notation(Notation::compactShort()),
Locale::getEnglish(),
999000,
u"999K");
assertFormatSingle(
u"Compact Rounding",
NumberFormatter::with().notation(Notation::compactShort()),
Locale::getEnglish(),
999900,
u"1M");
assertFormatSingle(
u"Compact Rounding",
NumberFormatter::with().notation(Notation::compactShort()),
Locale::getEnglish(),
9900000,
u"9.9M");
assertFormatSingle(
u"Compact Rounding",
NumberFormatter::with().notation(Notation::compactShort()),
Locale::getEnglish(),
9990000,
u"10M");
}
void NumberFormatterApiTest::unitMeasure() {
assertFormatDescending(
u"Meters Short",
NumberFormatter::with().adoptUnit(new MeasureUnit(METER)),
Locale::getEnglish(),
u"87,650 m",
u"8,765 m",
u"876.5 m",
u"87.65 m",
u"8.765 m",
u"0.8765 m",
u"0.08765 m",
u"0.008765 m",
u"0 m");
assertFormatDescending(
u"Meters Long",
NumberFormatter::with().adoptUnit(new MeasureUnit(METER))
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
Locale::getEnglish(),
u"87,650 meters",
u"8,765 meters",
u"876.5 meters",
u"87.65 meters",
u"8.765 meters",
u"0.8765 meters",
u"0.08765 meters",
u"0.008765 meters",
u"0 meters");
assertFormatDescending(
u"Compact Meters Long",
NumberFormatter::with().notation(Notation::compactLong())
.adoptUnit(new MeasureUnit(METER))
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
Locale::getEnglish(),
u"88 thousand meters",
u"8.8 thousand meters",
u"876 meters",
u"88 meters",
u"8.8 meters",
u"0.88 meters",
u"0.088 meters",
u"0.0088 meters",
u"0 meters");
// TODO: Implement Measure in C++
// assertFormatSingleMeasure(
// u"Meters with Measure Input",
// NumberFormatter::with().unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
// Locale::getEnglish(),
// new Measure(5.43, new MeasureUnit(METER)),
// u"5.43 meters");
// TODO: Implement Measure in C++
// assertFormatSingleMeasure(
// u"Measure format method takes precedence over fluent chain",
// NumberFormatter::with().adoptUnit(new MeasureUnit(METER)),
// Locale::getEnglish(),
// new Measure(5.43, USD),
// u"$5.43");
assertFormatSingle(
u"Meters with Negative Sign",
NumberFormatter::with().adoptUnit(new MeasureUnit(METER)),
Locale::getEnglish(),
-9876543.21,
u"-9,876,543.21 m");
// The locale string "सान" appears only in brx.txt:
assertFormatSingle(
u"Interesting Data Fallback 1",
NumberFormatter::with().adoptUnit(new MeasureUnit(DAY))
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
Locale::createFromName("brx"),
5.43,
u"5.43 सान");
// Requires following the alias from unitsNarrow to unitsShort:
assertFormatSingle(
u"Interesting Data Fallback 2",
NumberFormatter::with().adoptUnit(new MeasureUnit(DAY))
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
Locale::createFromName("brx"),
5.43,
u"5.43 d");
// en_001.txt has a unitsNarrow/area/square-meter table, but table does not contain the OTHER unit,
// requiring fallback to the root.
assertFormatSingle(
u"Interesting Data Fallback 3",
NumberFormatter::with().adoptUnit(new MeasureUnit(SQUARE_METER))
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
Locale::createFromName("en-GB"),
5.43,
u"5.43 m²");
// es_US has "{0}°" for unitsNarrow/temperature/FAHRENHEIT.
// NOTE: This example is in the documentation.
assertFormatSingle(
u"Difference between Narrow and Short (Narrow Version)",
NumberFormatter::with().adoptUnit(new MeasureUnit(FAHRENHEIT))
.unitWidth(UNUM_UNIT_WIDTH_NARROW),
Locale("es-US"),
5.43,
u"5.43°");
assertFormatSingle(
u"Difference between Narrow and Short (Short Version)",
NumberFormatter::with().adoptUnit(new MeasureUnit(FAHRENHEIT))
.unitWidth(UNUM_UNIT_WIDTH_SHORT),
Locale("es-US"),
5.43,
u"5.43 °F");
}
void NumberFormatterApiTest::unitCurrency() {
assertFormatDescending(
u"Currency",
NumberFormatter::with().unit(GBP),
Locale::getEnglish(),
u"£87,650.00",
u"£8,765.00",
u"£876.50",
u"£87.65",
u"£8.76",
u"£0.88",
u"£0.09",
u"£0.01",
u"£0.00");
assertFormatDescending(
u"Currency ISO",
NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
Locale::getEnglish(),
u"GBP 87,650.00",
u"GBP 8,765.00",
u"GBP 876.50",
u"GBP 87.65",
u"GBP 8.76",
u"GBP 0.88",
u"GBP 0.09",
u"GBP 0.01",
u"GBP 0.00");
assertFormatDescending(
u"Currency Long Name",
NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
Locale::getEnglish(),
u"87,650.00 British pounds",
u"8,765.00 British pounds",
u"876.50 British pounds",
u"87.65 British pounds",
u"8.76 British pounds",
u"0.88 British pounds",
u"0.09 British pounds",
u"0.01 British pounds",
u"0.00 British pounds");
assertFormatDescending(
u"Currency Hidden",
NumberFormatter::with().unit(GBP).unitWidth(UNUM_UNIT_WIDTH_HIDDEN),
Locale::getEnglish(),
u"87,650.00",
u"8,765.00",
u"876.50",
u"87.65",
u"8.76",
u"0.88",
u"0.09",
u"0.01",
u"0.00");
// TODO: Implement Measure in C++
// assertFormatSingleMeasure(
// u"Currency with CurrencyAmount Input",
// NumberFormatter::with(),
// Locale::getEnglish(),
// new CurrencyAmount(5.43, GBP),
// u"£5.43");
// TODO: Enable this test when DecimalFormat wrapper is done.
// assertFormatSingle(
// u"Currency Long Name from Pattern Syntax", NumberFormatter.fromDecimalFormat(
// PatternStringParser.parseToProperties("0 ¤¤¤"),
// DecimalFormatSymbols.getInstance(Locale::getEnglish()),
// null).unit(GBP), Locale::getEnglish(), 1234567.89, u"1234568 British pounds");
assertFormatSingle(
u"Currency with Negative Sign",
NumberFormatter::with().unit(GBP),
Locale::getEnglish(),
-9876543.21,
u"-£9,876,543.21");
}
void NumberFormatterApiTest::unitPercent() {
assertFormatDescending(
u"Percent",
NumberFormatter::with().unit(NoUnit::percent()),
Locale::getEnglish(),
u"87,650%",
u"8,765%",
u"876.5%",
u"87.65%",
u"8.765%",
u"0.8765%",
u"0.08765%",
u"0.008765%",
u"0%");
assertFormatDescending(
u"Permille",
NumberFormatter::with().unit(NoUnit::permille()),
Locale::getEnglish(),
u"87,650‰",
u"8,765‰",
u"876.5‰",
u"87.65‰",
u"8.765‰",
u"0.8765‰",
u"0.08765‰",
u"0.008765‰",
u"0‰");
assertFormatSingle(
u"NoUnit Base",
NumberFormatter::with().unit(NoUnit::base()),
Locale::getEnglish(),
51423,
u"51,423");
assertFormatSingle(
u"Percent with Negative Sign",
NumberFormatter::with().unit(NoUnit::percent()),
Locale::getEnglish(),
-98.7654321,
u"-98.765432%");
}
void NumberFormatterApiTest::roundingFraction() {
assertFormatDescending(
u"Integer",
NumberFormatter::with().rounding(Rounder::integer()),
Locale::getEnglish(),
u"87,650",
u"8,765",
u"876",
u"88",
u"9",
u"1",
u"0",
u"0",
u"0");
assertFormatDescending(
u"Fixed Fraction",
NumberFormatter::with().rounding(Rounder::fixedFraction(3)),
Locale::getEnglish(),
u"87,650.000",
u"8,765.000",
u"876.500",
u"87.650",
u"8.765",
u"0.876",
u"0.088",
u"0.009",
u"0.000");
assertFormatDescending(
u"Min Fraction",
NumberFormatter::with().rounding(Rounder::minFraction(1)),
Locale::getEnglish(),
u"87,650.0",
u"8,765.0",
u"876.5",
u"87.65",
u"8.765",
u"0.8765",
u"0.08765",
u"0.008765",
u"0.0");
assertFormatDescending(
u"Max Fraction",
NumberFormatter::with().rounding(Rounder::maxFraction(1)),
Locale::getEnglish(),
u"87,650",
u"8,765",
u"876.5",
u"87.6",
u"8.8",
u"0.9",
u"0.1",
u"0",
u"0");
assertFormatDescending(
u"Min/Max Fraction",
NumberFormatter::with().rounding(Rounder::minMaxFraction(1, 3)),
Locale::getEnglish(),
u"87,650.0",
u"8,765.0",
u"876.5",
u"87.65",
u"8.765",
u"0.876",
u"0.088",
u"0.009",
u"0.0");
}
void NumberFormatterApiTest::roundingFigures() {
assertFormatSingle(
u"Fixed Significant",
NumberFormatter::with().rounding(Rounder::fixedDigits(3)),
Locale::getEnglish(),
-98,
u"-98.0");
assertFormatSingle(
u"Fixed Significant Rounding",
NumberFormatter::with().rounding(Rounder::fixedDigits(3)),
Locale::getEnglish(),
-98.7654321,
u"-98.8");
assertFormatSingle(
u"Fixed Significant Zero",
NumberFormatter::with().rounding(Rounder::fixedDigits(3)),
Locale::getEnglish(),
0,
u"0.00");
assertFormatSingle(
u"Min Significant",
NumberFormatter::with().rounding(Rounder::minDigits(2)),
Locale::getEnglish(),
-9,
u"-9.0");
assertFormatSingle(
u"Max Significant",
NumberFormatter::with().rounding(Rounder::maxDigits(4)),
Locale::getEnglish(),
98.7654321,
u"98.77");
assertFormatSingle(
u"Min/Max Significant",
NumberFormatter::with().rounding(Rounder::minMaxDigits(3, 4)),
Locale::getEnglish(),
9.99999,
u"10.0");
}
void NumberFormatterApiTest::roundingFractionFigures() {
assertFormatDescending(
u"Basic Significant", // for comparison
NumberFormatter::with().rounding(Rounder::maxDigits(2)),
Locale::getEnglish(),
u"88,000",
u"8,800",
u"880",
u"88",
u"8.8",
u"0.88",
u"0.088",
u"0.0088",
u"0");
assertFormatDescending(
u"FracSig minMaxFrac minSig",
NumberFormatter::with().rounding(Rounder::minMaxFraction(1, 2).withMinDigits(3)),
Locale::getEnglish(),
u"87,650.0",
u"8,765.0",
u"876.5",
u"87.65",
u"8.76",
u"0.876", // minSig beats maxFrac
u"0.0876", // minSig beats maxFrac
u"0.00876", // minSig beats maxFrac
u"0.0");
assertFormatDescending(
u"FracSig minMaxFrac maxSig A",
NumberFormatter::with().rounding(Rounder::minMaxFraction(1, 3).withMaxDigits(2)),
Locale::getEnglish(),
u"88,000.0", // maxSig beats maxFrac
u"8,800.0", // maxSig beats maxFrac
u"880.0", // maxSig beats maxFrac
u"88.0", // maxSig beats maxFrac
u"8.8", // maxSig beats maxFrac
u"0.88", // maxSig beats maxFrac
u"0.088",
u"0.009",
u"0.0");
assertFormatDescending(
u"FracSig minMaxFrac maxSig B",
NumberFormatter::with().rounding(Rounder::fixedFraction(2).withMaxDigits(2)),
Locale::getEnglish(),
u"88,000.00", // maxSig beats maxFrac
u"8,800.00", // maxSig beats maxFrac
u"880.00", // maxSig beats maxFrac
u"88.00", // maxSig beats maxFrac
u"8.80", // maxSig beats maxFrac
u"0.88",
u"0.09",
u"0.01",
u"0.00");
}
void NumberFormatterApiTest::roundingOther() {
assertFormatDescending(
u"Rounding None",
NumberFormatter::with().rounding(Rounder::unlimited()),
Locale::getEnglish(),
u"87,650",
u"8,765",
u"876.5",
u"87.65",
u"8.765",
u"0.8765",
u"0.08765",
u"0.008765",
u"0");
assertFormatDescending(
u"Increment",
NumberFormatter::with().rounding(Rounder::increment(0.5).withMinFraction(1)),
Locale::getEnglish(),
u"87,650.0",
u"8,765.0",
u"876.5",
u"87.5",
u"9.0",
u"1.0",
u"0.0",
u"0.0",
u"0.0");
assertFormatDescending(
u"Increment with Min Fraction",
NumberFormatter::with().rounding(Rounder::increment(0.5).withMinFraction(2)),
Locale::getEnglish(),
u"87,650.00",
u"8,765.00",
u"876.50",
u"87.50",
u"9.00",
u"1.00",
u"0.00",
u"0.00",
u"0.00");
assertFormatDescending(
u"Currency Standard",
NumberFormatter::with().rounding(Rounder::currency(UCurrencyUsage::UCURR_USAGE_STANDARD))
.unit(CZK),
Locale::getEnglish(),
u"CZK 87,650.00",
u"CZK 8,765.00",
u"CZK 876.50",
u"CZK 87.65",
u"CZK 8.76",
u"CZK 0.88",
u"CZK 0.09",
u"CZK 0.01",
u"CZK 0.00");
assertFormatDescending(
u"Currency Cash",
NumberFormatter::with().rounding(Rounder::currency(UCurrencyUsage::UCURR_USAGE_CASH))
.unit(CZK),
Locale::getEnglish(),
u"CZK 87,650",
u"CZK 8,765",
u"CZK 876",
u"CZK 88",
u"CZK 9",
u"CZK 1",
u"CZK 0",
u"CZK 0",
u"CZK 0");
assertFormatDescending(
u"Currency Cash with Nickel Rounding",
NumberFormatter::with().rounding(Rounder::currency(UCurrencyUsage::UCURR_USAGE_CASH))
.unit(CAD),
Locale::getEnglish(),
u"CA$87,650.00",
u"CA$8,765.00",
u"CA$876.50",
u"CA$87.65",
u"CA$8.75",
u"CA$0.90",
u"CA$0.10",
u"CA$0.00",
u"CA$0.00");
assertFormatDescending(
u"Currency not in top-level fluent chain",
NumberFormatter::with().rounding(
Rounder::currency(UCurrencyUsage::UCURR_USAGE_CASH).withCurrency(CZK)),
Locale::getEnglish(),
u"87,650",
u"8,765",
u"876",
u"88",
u"9",
u"1",
u"0",
u"0",
u"0");
// NOTE: Other tests cover the behavior of the other rounding modes.
assertFormatDescending(
u"Rounding Mode CEILING",
NumberFormatter::with().rounding(Rounder::integer().withMode(UNumberFormatRoundingMode::UNUM_ROUND_CEILING)),
Locale::getEnglish(),
u"87,650",
u"8,765",
u"877",
u"88",
u"9",
u"1",
u"1",
u"1",
u"0");
}
void NumberFormatterApiTest::grouping() {
assertFormatDescendingBig(
u"Western Grouping",
NumberFormatter::with().grouping(Grouper::defaults()),
Locale::getEnglish(),
u"87,650,000",
u"8,765,000",
u"876,500",
u"87,650",
u"8,765",
u"876.5",
u"87.65",
u"8.765",
u"0");
assertFormatDescendingBig(
u"Indic Grouping",
NumberFormatter::with().grouping(Grouper::defaults()),
Locale("en-IN"),
u"8,76,50,000",
u"87,65,000",
u"8,76,500",
u"87,650",
u"8,765",
u"876.5",
u"87.65",
u"8.765",
u"0");
assertFormatDescendingBig(
u"Western Grouping, Wide",
NumberFormatter::with().grouping(Grouper::minTwoDigits()),
Locale::getEnglish(),
u"87,650,000",
u"8,765,000",
u"876,500",
u"87,650",
u"8765",
u"876.5",
u"87.65",
u"8.765",
u"0");
assertFormatDescendingBig(
u"Indic Grouping, Wide",
NumberFormatter::with().grouping(Grouper::minTwoDigits()),
Locale("en-IN"),
u"8,76,50,000",
u"87,65,000",
u"8,76,500",
u"87,650",
u"8765",
u"876.5",
u"87.65",
u"8.765",
u"0");
assertFormatDescendingBig(
u"No Grouping",
NumberFormatter::with().grouping(Grouper::none()),
Locale("en-IN"),
u"87650000",
u"8765000",
u"876500",
u"87650",
u"8765",
u"876.5",
u"87.65",
u"8.765",
u"0");
}
void NumberFormatterApiTest::padding() {
assertFormatDescending(
u"Padding",
NumberFormatter::with().padding(Padder::none()),
Locale::getEnglish(),
u"87,650",
u"8,765",
u"876.5",
u"87.65",
u"8.765",
u"0.8765",
u"0.08765",
u"0.008765",
u"0");
assertFormatDescending(
u"Padding",
NumberFormatter::with().padding(
Padder::codePoints(
'*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
Locale::getEnglish(),
u"**87,650",
u"***8,765",
u"***876.5",
u"***87.65",
u"***8.765",
u"**0.8765",
u"*0.08765",
u"0.008765",
u"*******0");
assertFormatDescending(
u"Padding with code points",
NumberFormatter::with().padding(
Padder::codePoints(
0x101E4, 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
Locale::getEnglish(),
u"𐇤𐇤87,650",
u"𐇤𐇤𐇤8,765",
u"𐇤𐇤𐇤876.5",
u"𐇤𐇤𐇤87.65",
u"𐇤𐇤𐇤8.765",
u"𐇤𐇤0.8765",
u"𐇤0.08765",
u"0.008765",
u"𐇤𐇤𐇤𐇤𐇤𐇤𐇤0");
assertFormatDescending(
u"Padding with wide digits",
NumberFormatter::with().padding(
Padder::codePoints(
'*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX))
.adoptSymbols(new NumberingSystem(MATHSANB)),
Locale::getEnglish(),
u"**𝟴𝟳,𝟲𝟱𝟬",
u"***𝟴,𝟳𝟲𝟱",
u"***𝟴𝟳𝟲.𝟱",
u"***𝟴𝟳.𝟲𝟱",
u"***𝟴.𝟳𝟲𝟱",
u"**𝟬.𝟴𝟳𝟲𝟱",
u"*𝟬.𝟬𝟴𝟳𝟲𝟱",
u"𝟬.𝟬𝟬𝟴𝟳𝟲𝟱",
u"*******𝟬");
assertFormatDescending(
u"Padding with currency spacing",
NumberFormatter::with().padding(
Padder::codePoints(
'*', 10, PadPosition::UNUM_PAD_AFTER_PREFIX))
.unit(GBP)
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
Locale::getEnglish(),
u"GBP 87,650.00",
u"GBP 8,765.00",
u"GBP*876.50",
u"GBP**87.65",
u"GBP***8.76",
u"GBP***0.88",
u"GBP***0.09",
u"GBP***0.01",
u"GBP***0.00");
assertFormatSingle(
u"Pad Before Prefix",
NumberFormatter::with().padding(
Padder::codePoints(
'*', 8, PadPosition::UNUM_PAD_BEFORE_PREFIX)),
Locale::getEnglish(),
-88.88,
u"**-88.88");
assertFormatSingle(
u"Pad After Prefix",
NumberFormatter::with().padding(
Padder::codePoints(
'*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
Locale::getEnglish(),
-88.88,
u"-**88.88");
assertFormatSingle(
u"Pad Before Suffix",
NumberFormatter::with().padding(
Padder::codePoints(
'*', 8, PadPosition::UNUM_PAD_BEFORE_SUFFIX)).unit(NoUnit::percent()),
Locale::getEnglish(),
88.88,
u"88.88**%");
assertFormatSingle(
u"Pad After Suffix",
NumberFormatter::with().padding(
Padder::codePoints(
'*', 8, PadPosition::UNUM_PAD_AFTER_SUFFIX)).unit(NoUnit::percent()),
Locale::getEnglish(),
88.88,
u"88.88%**");
assertFormatSingle(
u"Currency Spacing with Zero Digit Padding Broken",
NumberFormatter::with().padding(
Padder::codePoints(
'0', 12, PadPosition::UNUM_PAD_AFTER_PREFIX))
.unit(GBP)
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
Locale::getEnglish(),
514.23,
u"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
}
void NumberFormatterApiTest::integerWidth() {
assertFormatDescending(
u"Integer Width Default",
NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1)),
Locale::getEnglish(),
u"87,650",
u"8,765",
u"876.5",
u"87.65",
u"8.765",
u"0.8765",
u"0.08765",
u"0.008765",
u"0");
assertFormatDescending(
u"Integer Width Zero Fill 0",
NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(0)),
Locale::getEnglish(),
u"87,650",
u"8,765",
u"876.5",
u"87.65",
u"8.765",
u".8765",
u".08765",
u".008765",
u""); // TODO: Avoid the empty string here?
assertFormatDescending(
u"Integer Width Zero Fill 3",
NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(3)),
Locale::getEnglish(),
u"87,650",
u"8,765",
u"876.5",
u"087.65",
u"008.765",
u"000.8765",
u"000.08765",
u"000.008765",
u"000");
assertFormatDescending(
u"Integer Width Max 3",
NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1).truncateAt(3)),
Locale::getEnglish(),
u"650",
u"765",
u"876.5",
u"87.65",
u"8.765",
u"0.8765",
u"0.08765",
u"0.008765",
u"0");
assertFormatDescending(
u"Integer Width Fixed 2",
NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
Locale::getEnglish(),
u"50",
u"65",
u"76.5",
u"87.65",
u"08.765",
u"00.8765",
u"00.08765",
u"00.008765",
u"00");
}
void NumberFormatterApiTest::symbols() {
assertFormatDescending(
u"French Symbols with Japanese Data 1",
NumberFormatter::with().symbols(FRENCH_SYMBOLS),
Locale::getJapan(),
u"87 650",
u"8 765",
u"876,5",
u"87,65",
u"8,765",
u"0,8765",
u"0,08765",
u"0,008765",
u"0");
assertFormatSingle(
u"French Symbols with Japanese Data 2",
NumberFormatter::with().notation(Notation::compactShort()).symbols(FRENCH_SYMBOLS),
Locale::getJapan(),
12345,
u"1,2\u4E07");
assertFormatDescending(
u"Latin Numbering System with Arabic Data",
NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
Locale("ar"),
u"US$ 87,650.00",
u"US$ 8,765.00",
u"US$ 876.50",
u"US$ 87.65",
u"US$ 8.76",
u"US$ 0.88",
u"US$ 0.09",
u"US$ 0.01",
u"US$ 0.00");
assertFormatDescending(
u"Math Numbering System with French Data",
NumberFormatter::with().adoptSymbols(new NumberingSystem(MATHSANB)),
Locale::getFrench(),
u"𝟴𝟳 𝟲𝟱𝟬",
u"𝟴 𝟳𝟲𝟱",
u"𝟴𝟳𝟲,𝟱",
u"𝟴𝟳,𝟲𝟱",
u"𝟴,𝟳𝟲𝟱",
u"𝟬,𝟴𝟳𝟲𝟱",
u"𝟬,𝟬𝟴𝟳𝟲𝟱",
u"𝟬,𝟬𝟬𝟴𝟳𝟲𝟱",
u"𝟬");
assertFormatSingle(
u"Swiss Symbols (used in documentation)",
NumberFormatter::with().symbols(SWISS_SYMBOLS),
Locale::getEnglish(),
12345.67,
u"12345.67");
assertFormatSingle(
u"Myanmar Symbols (used in documentation)",
NumberFormatter::with().symbols(MYANMAR_SYMBOLS),
Locale::getEnglish(),
12345.67,
u"\u1041\u1042,\u1043\u1044\u1045.\u1046\u1047");
// NOTE: Locale ar puts ¤ after the number in NS arab but before the number in NS latn.
assertFormatSingle(
u"Currency symbol should precede number in ar with NS latn",
NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
Locale("ar"),
12345.67,
u"US$ 12,345.67");
assertFormatSingle(
u"Currency symbol should precede number in ar@numbers=latn",
NumberFormatter::with().unit(USD),
Locale("ar@numbers=latn"),
12345.67,
u"US$ 12,345.67");
assertFormatSingle(
u"Currency symbol should follow number in ar with NS arab",
NumberFormatter::with().unit(USD),
Locale("ar"),
12345.67,
u"١٢٬٣٤٥٫٦٧ US$");
assertFormatSingle(
u"Currency symbol should follow number in ar@numbers=arab",
NumberFormatter::with().unit(USD),
Locale("ar@numbers=arab"),
12345.67,
u"١٢٬٣٤٥٫٦٧ US$");
UErrorCode status = U_ZERO_ERROR;
DecimalFormatSymbols symbols = SWISS_SYMBOLS;
UnlocalizedNumberFormatter f = NumberFormatter::with().symbols(symbols);
symbols.setSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol, u"!", status);
assertFormatSingle(
u"Symbols object should be copied", f, Locale::getEnglish(), 12345.67, u"12345.67");
assertFormatSingle(
u"The last symbols setter wins",
NumberFormatter::with().symbols(symbols).adoptSymbols(new NumberingSystem(LATN)),
Locale::getEnglish(),
12345.67,
u"12,345.67");
assertFormatSingle(
u"The last symbols setter wins",
NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).symbols(symbols),
Locale::getEnglish(),
12345.67,
u"12!345.67");
}
// TODO: Enable if/when currency symbol override is added.
//void NumberFormatterTest::symbolsOverride() {
// DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(Locale::getEnglish());
// dfs.setCurrencySymbol("@");
// dfs.setInternationalCurrencySymbol("foo");
// assertFormatSingle(
// u"Custom Short Currency Symbol",
// NumberFormatter::with().unit(Currency.getInstance("XXX")).symbols(dfs),
// Locale::getEnglish(),
// 12.3,
// u"@ 12.30");
//}
void NumberFormatterApiTest::sign() {
assertFormatSingle(
u"Sign Auto Positive",
NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
Locale::getEnglish(),
444444,
u"444,444");
assertFormatSingle(
u"Sign Auto Negative",
NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
Locale::getEnglish(),
-444444,
u"-444,444");
assertFormatSingle(
u"Sign Always Positive",
NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
Locale::getEnglish(),
444444,
u"+444,444");
assertFormatSingle(
u"Sign Always Negative",
NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
Locale::getEnglish(),
-444444,
u"-444,444");
assertFormatSingle(
u"Sign Never Positive",
NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
Locale::getEnglish(),
444444,
u"444,444");
assertFormatSingle(
u"Sign Never Negative",
NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
Locale::getEnglish(),
-444444,
u"444,444");
assertFormatSingle(
u"Sign Accounting Positive",
NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
Locale::getEnglish(),
444444,
u"$444,444.00");
assertFormatSingle(
u"Sign Accounting Negative",
NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
Locale::getEnglish(),
-444444,
u"($444,444.00)");
assertFormatSingle(
u"Sign Accounting-Always Positive",
NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
Locale::getEnglish(),
444444,
u"+$444,444.00");
assertFormatSingle(
u"Sign Accounting-Always Negative",
NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
Locale::getEnglish(),
-444444,
u"($444,444.00)");
assertFormatSingle(
u"Sign Accounting Negative Hidden",
NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
.unit(USD)
.unitWidth(UNUM_UNIT_WIDTH_HIDDEN),
Locale::getEnglish(),
-444444,
u"(444,444.00)");
}
void NumberFormatterApiTest::decimal() {
assertFormatDescending(
u"Decimal Default",
NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_AUTO),
Locale::getEnglish(),
u"87,650",
u"8,765",
u"876.5",
u"87.65",
u"8.765",
u"0.8765",
u"0.08765",
u"0.008765",
u"0");
assertFormatDescending(
u"Decimal Always Shown",
NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_ALWAYS),
Locale::getEnglish(),
u"87,650.",
u"8,765.",
u"876.5",
u"87.65",
u"8.765",
u"0.8765",
u"0.08765",
u"0.008765",
u"0.");
}
void NumberFormatterApiTest::locale() {
// Coverage for the locale setters.
UErrorCode status = U_ZERO_ERROR;
UnicodeString actual = NumberFormatter::withLocale(Locale::getFrench()).formatInt(1234, status)
.toString();
assertEquals("Locale withLocale()", u"1 234", actual);
}
void NumberFormatterApiTest::formatTypes() {
UErrorCode status = U_ZERO_ERROR;
LocalizedNumberFormatter formatter = NumberFormatter::withLocale(Locale::getEnglish());
const char* str1 = "98765432123456789E1";
UnicodeString actual = formatter.formatDecimal(str1, status).toString();
assertEquals("Format decNumber", u"987,654,321,234,567,890", actual);
}
void NumberFormatterApiTest::errors() {
LocalizedNumberFormatter lnf = NumberFormatter::withLocale(Locale::getEnglish()).rounding(
Rounder::fixedFraction(
-1));
{
UErrorCode status1 = U_ZERO_ERROR;
UErrorCode status2 = U_ZERO_ERROR;
FormattedNumber fn = lnf.formatInt(1, status1);
assertEquals(
"Should fail with U_ILLEGAL_ARGUMENT_ERROR since rounder is not legal",
U_ILLEGAL_ARGUMENT_ERROR,
status1);
FieldPosition fp;
fn.populateFieldPosition(fp, status2);
assertEquals(
"Should fail with U_ILLEGAL_ARGUMENT_ERROR on terminal method",
U_ILLEGAL_ARGUMENT_ERROR,
status2);
}
{
UErrorCode status = U_ZERO_ERROR;
lnf.copyErrorTo(status);
assertEquals(
"Should fail with U_ILLEGAL_ARGUMENT_ERROR since rounder is not legal",
U_ILLEGAL_ARGUMENT_ERROR,
status);
}
}
void NumberFormatterApiTest::assertFormatDescending(const UnicodeString &message,
const UnlocalizedNumberFormatter &f,
Locale locale, ...) {
va_list args;
va_start(args, locale);
static double inputs[] = {87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0};
const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
UErrorCode status = U_ZERO_ERROR;
for (int16_t i = 0; i < 9; i++) {
char16_t caseNumber = u'0' + i;
double d = inputs[i];
UnicodeString expected = va_arg(args, const char16_t*);
UnicodeString actual1 = l1.formatDouble(d, status).toString();
assertSuccess(message + u": Unsafe Path: " + caseNumber, status);
assertEquals(message + u": Unsafe Path: " + caseNumber, expected, actual1);
UnicodeString actual2 = l2.formatDouble(d, status).toString();
assertSuccess(message + u": Safe Path: " + caseNumber, status);
assertEquals(message + u": Safe Path: " + caseNumber, expected, actual2);
}
}
void NumberFormatterApiTest::assertFormatDescendingBig(const UnicodeString &message,
const UnlocalizedNumberFormatter &f,
Locale locale, ...) {
va_list args;
va_start(args, locale);
static double inputs[] = {87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0};
const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
UErrorCode status = U_ZERO_ERROR;
for (int16_t i = 0; i < 9; i++) {
char16_t caseNumber = u'0' + i;
double d = inputs[i];
UnicodeString expected = va_arg(args, const char16_t*);
UnicodeString actual1 = l1.formatDouble(d, status).toString();
assertSuccess(message + u": Unsafe Path: " + caseNumber, status);
assertEquals(message + u": Unsafe Path: " + caseNumber, expected, actual1);
UnicodeString actual2 = l2.formatDouble(d, status).toString();
assertSuccess(message + u": Safe Path: " + caseNumber, status);
assertEquals(message + u": Safe Path: " + caseNumber, expected, actual2);
}
}
void NumberFormatterApiTest::assertFormatSingle(const UnicodeString &message,
const UnlocalizedNumberFormatter &f, Locale locale,
double input, const UnicodeString &expected) {
const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
UErrorCode status = U_ZERO_ERROR;
UnicodeString actual1 = l1.formatDouble(input, status).toString();
assertSuccess(message + u": Unsafe Path", status);
assertEquals(message + u": Unsafe Path", expected, actual1);
UnicodeString actual2 = l2.formatDouble(input, status).toString();
assertSuccess(message + u": Safe Path", status);
assertEquals(message + u": Safe Path", expected, actual2);
}
#endif /* #if !UCONFIG_NO_FORMATTING */