From: Shane Carr Date: Thu, 8 Feb 2018 06:06:08 +0000 (+0000) Subject: ICU-13443 Change digit width types to int16_t and change maximum setting to 999.... X-Git-Tag: release-61-rc~116 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ad348faca9e0c29af6dd602e026998fd773ae2b7;p=icu ICU-13443 Change digit width types to int16_t and change maximum setting to 999. Adds test for this behavior in ICU4C. Also adds the new error code for ICU-13577 . X-SVN-Rev: 40866 --- diff --git a/icu4c/source/common/unicode/utypes.h b/icu4c/source/common/unicode/utypes.h index d60450b5a56..b7796cb7dda 100644 --- a/icu4c/source/common/unicode/utypes.h +++ b/icu4c/source/common/unicode/utypes.h @@ -539,6 +539,7 @@ typedef enum UErrorCode { U_DEFAULT_KEYWORD_MISSING, /**< Missing DEFAULT rule in plural rules */ U_DECIMAL_NUMBER_SYNTAX_ERROR, /**< Decimal number syntax error */ U_FORMAT_INEXACT_ERROR, /**< Cannot format a number exactly and rounding mode is ROUND_UNNECESSARY @stable ICU 4.8 */ + U_NUMBER_ARG_OUTOFBOUNDS_ERROR, /**< The argument to a NumberFormatter helper method was out of bounds; the bounds are usually 0 to 999. @draft ICU 61 */ #ifndef U_HIDE_DEPRECATED_API /** * One more than the highest normal formatting API error code. diff --git a/icu4c/source/common/utypes.cpp b/icu4c/source/common/utypes.cpp index 8f5791be160..5d6a0504ba6 100644 --- a/icu4c/source/common/utypes.cpp +++ b/icu4c/source/common/utypes.cpp @@ -125,7 +125,8 @@ _uFmtErrorName[U_FMT_PARSE_ERROR_LIMIT - U_FMT_PARSE_ERROR_START] = { "U_UNDEFINED_KEYWORD", "U_DEFAULT_KEYWORD_MISSING", "U_DECIMAL_NUMBER_SYNTAX_ERROR", - "U_FORMAT_INEXACT_ERROR" + "U_FORMAT_INEXACT_ERROR", + "U_NUMBER_ARG_OUTOFBOUNDS_ERROR" }; static const char * const diff --git a/icu4c/source/i18n/number_integerwidth.cpp b/icu4c/source/i18n/number_integerwidth.cpp index 10dacfc4acb..4a612273f5e 100644 --- a/icu4c/source/i18n/number_integerwidth.cpp +++ b/icu4c/source/i18n/number_integerwidth.cpp @@ -13,25 +13,28 @@ using namespace icu; using namespace icu::number; using namespace icu::number::impl; -IntegerWidth::IntegerWidth(int8_t minInt, int8_t maxInt) { +IntegerWidth::IntegerWidth(digits_t minInt, digits_t maxInt) { fUnion.minMaxInt.fMinInt = minInt; fUnion.minMaxInt.fMaxInt = maxInt; } IntegerWidth IntegerWidth::zeroFillTo(int32_t minInt) { if (minInt >= 0 && minInt <= kMaxIntFracSig) { - return {static_cast(minInt), -1}; + return {static_cast(minInt), -1}; } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } IntegerWidth IntegerWidth::truncateAt(int32_t maxInt) { if (fHasError) { return *this; } // No-op on error - if (maxInt >= 0 && maxInt <= kMaxIntFracSig) { - return {fUnion.minMaxInt.fMinInt, static_cast(maxInt)}; + digits_t minInt = fUnion.minMaxInt.fMinInt; + if (maxInt >= 0 && maxInt <= kMaxIntFracSig && minInt <= maxInt) { + return {minInt, static_cast(maxInt)}; + } else if (maxInt == -1) { + return {minInt, -1}; } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } diff --git a/icu4c/source/i18n/number_notation.cpp b/icu4c/source/i18n/number_notation.cpp index ff0cd9505de..f4ad333354d 100644 --- a/icu4c/source/i18n/number_notation.cpp +++ b/icu4c/source/i18n/number_notation.cpp @@ -54,13 +54,13 @@ Notation Notation::simple() { ScientificNotation ScientificNotation::withMinExponentDigits(int32_t minExponentDigits) const { - if (minExponentDigits >= 0 && minExponentDigits < kMaxIntFracSig) { + if (minExponentDigits >= 1 && minExponentDigits <= kMaxIntFracSig) { ScientificSettings settings = fUnion.scientific; - settings.fMinExponentDigits = (int8_t) minExponentDigits; + settings.fMinExponentDigits = static_cast(minExponentDigits); NotationUnion union_ = {settings}; return {NTN_SCIENTIFIC, union_}; } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } diff --git a/icu4c/source/i18n/number_padding.cpp b/icu4c/source/i18n/number_padding.cpp index a478af60541..b1db3490cd4 100644 --- a/icu4c/source/i18n/number_padding.cpp +++ b/icu4c/source/i18n/number_padding.cpp @@ -43,7 +43,7 @@ Padder Padder::codePoints(UChar32 cp, int32_t targetWidth, UNumberFormatPadPosit if (targetWidth >= 0) { return {cp, targetWidth, position}; } else { - return {U_NUMBER_PADDING_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } diff --git a/icu4c/source/i18n/number_rounding.cpp b/icu4c/source/i18n/number_rounding.cpp index 2f31727e994..fd4dafdf983 100644 --- a/icu4c/source/i18n/number_rounding.cpp +++ b/icu4c/source/i18n/number_rounding.cpp @@ -58,7 +58,7 @@ FractionRounder Rounder::fixedFraction(int32_t minMaxFractionPlaces) { if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= kMaxIntFracSig) { return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces); } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } @@ -66,7 +66,7 @@ FractionRounder Rounder::minFraction(int32_t minFractionPlaces) { if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) { return constructFraction(minFractionPlaces, -1); } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } @@ -74,7 +74,7 @@ FractionRounder Rounder::maxFraction(int32_t maxFractionPlaces) { if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) { return constructFraction(0, maxFractionPlaces); } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } @@ -83,40 +83,40 @@ FractionRounder Rounder::minMaxFraction(int32_t minFractionPlaces, int32_t maxFr minFractionPlaces <= maxFractionPlaces) { return constructFraction(minFractionPlaces, maxFractionPlaces); } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } Rounder Rounder::fixedDigits(int32_t minMaxSignificantDigits) { - if (minMaxSignificantDigits >= 0 && minMaxSignificantDigits <= kMaxIntFracSig) { + if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= kMaxIntFracSig) { return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits); } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } Rounder Rounder::minDigits(int32_t minSignificantDigits) { - if (minSignificantDigits >= 0 && minSignificantDigits <= kMaxIntFracSig) { + if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) { return constructSignificant(minSignificantDigits, -1); } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } Rounder Rounder::maxDigits(int32_t maxSignificantDigits) { - if (maxSignificantDigits >= 0 && maxSignificantDigits <= kMaxIntFracSig) { - return constructSignificant(0, maxSignificantDigits); + if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) { + return constructSignificant(1, maxSignificantDigits); } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } Rounder Rounder::minMaxDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) { - if (minSignificantDigits >= 0 && maxSignificantDigits <= kMaxIntFracSig && + if (minSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig && minSignificantDigits <= maxSignificantDigits) { return constructSignificant(minSignificantDigits, maxSignificantDigits); } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } @@ -124,7 +124,7 @@ IncrementRounder Rounder::increment(double roundingIncrement) { if (roundingIncrement > 0.0) { return constructIncrement(roundingIncrement, 0); } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } @@ -139,19 +139,19 @@ Rounder Rounder::withMode(RoundingMode roundingMode) const { Rounder FractionRounder::withMinDigits(int32_t minSignificantDigits) const { if (fType == RND_ERROR) { return *this; } // no-op in error state - if (minSignificantDigits >= 0 && minSignificantDigits <= kMaxIntFracSig) { + if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) { return constructFractionSignificant(*this, minSignificantDigits, -1); } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } Rounder FractionRounder::withMaxDigits(int32_t maxSignificantDigits) const { if (fType == RND_ERROR) { return *this; } // no-op in error state - if (maxSignificantDigits >= 0 && maxSignificantDigits <= kMaxIntFracSig) { + if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) { return constructFractionSignificant(*this, -1, maxSignificantDigits); } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } @@ -185,14 +185,14 @@ Rounder IncrementRounder::withMinFraction(int32_t minFrac) const { if (minFrac >= 0 && minFrac <= kMaxIntFracSig) { return constructIncrement(fUnion.increment.fIncrement, minFrac); } else { - return {U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR}; + return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } FractionRounder Rounder::constructFraction(int32_t minFrac, int32_t maxFrac) { FractionSignificantSettings settings; - settings.fMinFrac = static_cast (minFrac); - settings.fMaxFrac = static_cast (maxFrac); + settings.fMinFrac = static_cast(minFrac); + settings.fMaxFrac = static_cast(maxFrac); settings.fMinSig = -1; settings.fMaxSig = -1; RounderUnion union_; @@ -204,8 +204,8 @@ Rounder Rounder::constructSignificant(int32_t minSig, int32_t maxSig) { FractionSignificantSettings settings; settings.fMinFrac = -1; settings.fMaxFrac = -1; - settings.fMinSig = static_cast(minSig); - settings.fMaxSig = static_cast(maxSig); + settings.fMinSig = static_cast(minSig); + settings.fMaxSig = static_cast(maxSig); RounderUnion union_; union_.fracSig = settings; return {RND_SIGNIFICANT, union_, kDefaultMode}; @@ -214,8 +214,8 @@ Rounder Rounder::constructSignificant(int32_t minSig, int32_t maxSig) { Rounder Rounder::constructFractionSignificant(const FractionRounder &base, int32_t minSig, int32_t maxSig) { FractionSignificantSettings settings = base.fUnion.fracSig; - settings.fMinSig = static_cast(minSig); - settings.fMaxSig = static_cast(maxSig); + settings.fMinSig = static_cast(minSig); + settings.fMaxSig = static_cast(maxSig); RounderUnion union_; union_.fracSig = settings; return {RND_FRACTION_SIGNIFICANT, union_, kDefaultMode}; @@ -224,7 +224,7 @@ Rounder::constructFractionSignificant(const FractionRounder &base, int32_t minSi IncrementRounder Rounder::constructIncrement(double increment, int32_t minFrac) { IncrementSettings settings; settings.fIncrement = increment; - settings.fMinFrac = minFrac; + settings.fMinFrac = static_cast(minFrac); RounderUnion union_; union_.increment = settings; return {RND_INCREMENT, union_, kDefaultMode}; diff --git a/icu4c/source/i18n/number_types.h b/icu4c/source/i18n/number_types.h index e914ef71ac0..c01765e2cea 100644 --- a/icu4c/source/i18n/number_types.h +++ b/icu4c/source/i18n/number_types.h @@ -31,7 +31,7 @@ typedef UNumberFormatPadPosition PadPosition; typedef UNumberCompactStyle CompactStyle; // ICU4J Equivalent: RoundingUtils.MAX_INT_FRAC_SIG -static constexpr int32_t kMaxIntFracSig = 100; +static constexpr int32_t kMaxIntFracSig = 999; // ICU4J Equivalent: RoundingUtils.DEFAULT_ROUNDING_MODE static constexpr RoundingMode kDefaultMode = RoundingMode::UNUM_FOUND_HALFEVEN; @@ -42,10 +42,6 @@ static constexpr char16_t kFallbackPaddingString[] = u" "; // ICU4J Equivalent: NumberFormatterImpl.DEFAULT_CURRENCY static constexpr char16_t kDefaultCurrency[] = u"XXX"; -// FIXME: New error codes: -static constexpr UErrorCode U_NUMBER_DIGIT_WIDTH_OUTOFBOUNDS_ERROR = U_ILLEGAL_ARGUMENT_ERROR; -static constexpr UErrorCode U_NUMBER_PADDING_WIDTH_OUTOFBOUNDS_ERROR = U_ILLEGAL_ARGUMENT_ERROR; - // Forward declarations: class Modifier; diff --git a/icu4c/source/i18n/unicode/numberformatter.h b/icu4c/source/i18n/unicode/numberformatter.h index 4c4f542b4dd..be4593309e7 100644 --- a/icu4c/source/i18n/unicode/numberformatter.h +++ b/icu4c/source/i18n/unicode/numberformatter.h @@ -394,6 +394,21 @@ class IntegerWidth; namespace impl { +/** + * Datatype for minimum/maximum fraction digits. Must be able to hold kMaxIntFracSig. + * + * @internal + */ +typedef int16_t digits_t; + +/** + * Use a default threshold of 3. This means that the third time .format() is called, the data structures get built + * using the "safe" code path. The first two calls to .format() will trigger the unsafe code path. + * + * @internal + */ +static constexpr int32_t DEFAULT_THRESHOLD = 3; + // Forward declarations: class Padder; struct MacroProps; @@ -577,7 +592,7 @@ class U_I18N_API Notation : public UMemory { struct ScientificSettings { int8_t fEngineeringInterval; bool fRequireMinInt; - int8_t fMinExponentDigits; + impl::digits_t fMinExponentDigits; UNumberSignDisplay fExponentSignDisplay; } scientific; @@ -892,14 +907,14 @@ class U_I18N_API Rounder : public UMemory { union RounderUnion { struct FractionSignificantSettings { // For RND_FRACTION, RND_SIGNIFICANT, and RND_FRACTION_SIGNIFICANT - int8_t fMinFrac; - int8_t fMaxFrac; - int8_t fMinSig; - int8_t fMaxSig; + impl::digits_t fMinFrac; + impl::digits_t fMaxFrac; + impl::digits_t fMinSig; + impl::digits_t fMaxSig; } fracSig; struct IncrementSettings { double fIncrement; - int32_t fMinFrac; + impl::digits_t fMinFrac; } increment; // For RND_INCREMENT UCurrencyUsage currencyUsage; // For RND_CURRENCY UErrorCode errorCode; // For RND_ERROR @@ -1153,7 +1168,8 @@ class U_I18N_API IntegerWidth : public UMemory { * For example, with maxInt=3, the number 1234 will get printed as "234". * * @param maxInt - * The maximum number of places before the decimal separator. + * The maximum number of places before the decimal separator. maxInt == -1 means no + * truncation. * @return An IntegerWidth for passing to the NumberFormatter integerWidth() setter. * @draft ICU 60 * @see NumberFormatter @@ -1163,14 +1179,14 @@ class U_I18N_API IntegerWidth : public UMemory { private: union { struct { - int8_t fMinInt; - int8_t fMaxInt; + impl::digits_t fMinInt; + impl::digits_t fMaxInt; } minMaxInt; UErrorCode errorCode; } fUnion; bool fHasError = false; - IntegerWidth(int8_t minInt, int8_t maxInt); + IntegerWidth(impl::digits_t minInt, impl::digits_t maxInt); IntegerWidth(UErrorCode errorCode) { // NOLINT fUnion.errorCode = errorCode; @@ -1205,14 +1221,6 @@ class U_I18N_API IntegerWidth : public UMemory { namespace impl { -/** - * Use a default threshold of 3. This means that the third time .format() is called, the data structures get built - * using the "safe" code path. The first two calls to .format() will trigger the unsafe code path. - * - * @internal - */ -static constexpr int32_t DEFAULT_THRESHOLD = 3; - /** @internal */ class U_I18N_API SymbolsWrapper : public UMemory { public: diff --git a/icu4c/source/test/intltest/intltest.cpp b/icu4c/source/test/intltest/intltest.cpp index 5edf872d3b7..c45913796a4 100644 --- a/icu4c/source/test/intltest/intltest.cpp +++ b/icu4c/source/test/intltest/intltest.cpp @@ -2030,6 +2030,25 @@ UBool IntlTest::assertEquals(const char* message, return TRUE; } + +UBool IntlTest::assertEquals(const char* message, + UErrorCode expected, + UErrorCode actual) { + if (expected != actual) { + errln((UnicodeString)"FAIL: " + message + "; got " + + u_errorName(actual) + + "; expected " + u_errorName(expected)); + return FALSE; + } +#ifdef VERBOSE_ASSERTIONS + else { + logln((UnicodeString)"Ok: " + message + "; got " + u_errorName(actual)); + } +#endif + return TRUE; +} + + #if !UCONFIG_NO_FORMATTING UBool IntlTest::assertEquals(const char* message, const Formattable& expected, @@ -2105,6 +2124,16 @@ UBool IntlTest::assertEquals(const UnicodeString& message, int64_t actual) { return assertEquals(extractToAssertBuf(message), expected, actual); } +UBool IntlTest::assertEquals(const UnicodeString& message, + double expected, + double actual) { + return assertEquals(extractToAssertBuf(message), expected, actual); +} +UBool IntlTest::assertEquals(const UnicodeString& message, + UErrorCode expected, + UErrorCode actual) { + return assertEquals(extractToAssertBuf(message), expected, actual); +} #if !UCONFIG_NO_FORMATTING UBool IntlTest::assertEquals(const UnicodeString& message, diff --git a/icu4c/source/test/intltest/intltest.h b/icu4c/source/test/intltest/intltest.h index 1f7c80d4794..08765b707d0 100644 --- a/icu4c/source/test/intltest/intltest.h +++ b/icu4c/source/test/intltest/intltest.h @@ -289,13 +289,12 @@ public: UBool assertSuccess(const char* message, UErrorCode ec, UBool possibleDataError=FALSE, const char *file=NULL, int line=0); UBool assertEquals(const char* message, const UnicodeString& expected, const UnicodeString& actual, UBool possibleDataError=FALSE); - UBool assertEquals(const char* message, const char* expected, - const char* actual); - UBool assertEquals(const char* message, UBool expected, - UBool actual); + UBool assertEquals(const char* message, const char* expected, const char* actual); + UBool assertEquals(const char* message, UBool expected, UBool actual); UBool assertEquals(const char* message, int32_t expected, int32_t actual); UBool assertEquals(const char* message, int64_t expected, int64_t actual); UBool assertEquals(const char* message, double expected, double actual); + UBool assertEquals(const char* message, UErrorCode expected, UErrorCode actual); #if !UCONFIG_NO_FORMATTING UBool assertEquals(const char* message, const Formattable& expected, const Formattable& actual, UBool possibleDataError=FALSE); @@ -307,11 +306,12 @@ public: UBool assertSuccess(const UnicodeString& message, UErrorCode ec); UBool assertEquals(const UnicodeString& message, const UnicodeString& expected, const UnicodeString& actual, UBool possibleDataError=FALSE); - UBool assertEquals(const UnicodeString& message, const char* expected, - const char* actual); + UBool assertEquals(const UnicodeString& message, const char* expected, const char* actual); UBool assertEquals(const UnicodeString& message, UBool expected, UBool actual); UBool assertEquals(const UnicodeString& message, int32_t expected, int32_t actual); UBool assertEquals(const UnicodeString& message, int64_t expected, int64_t actual); + UBool assertEquals(const UnicodeString& message, double expected, double actual); + UBool assertEquals(const UnicodeString& message, UErrorCode expected, UErrorCode actual); virtual void runIndexedTest( int32_t index, UBool exec, const char* &name, char* par = NULL ); // overide ! diff --git a/icu4c/source/test/intltest/numbertest.h b/icu4c/source/test/intltest/numbertest.h index 9d4ffb7cef0..5b4030a9494 100644 --- a/icu4c/source/test/intltest/numbertest.h +++ b/icu4c/source/test/intltest/numbertest.h @@ -63,6 +63,7 @@ class NumberFormatterApiTest : public IntlTest { void locale(); void formatTypes(); void errors(); + void validRanges(); void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0); diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp index 62db705eac7..2d625877f30 100644 --- a/icu4c/source/test/intltest/numbertest_api.cpp +++ b/icu4c/source/test/intltest/numbertest_api.cpp @@ -76,6 +76,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha TESTCASE_AUTO(locale); TESTCASE_AUTO(formatTypes); TESTCASE_AUTO(errors); + TESTCASE_AUTO(validRanges); TESTCASE_AUTO_END; } @@ -1748,27 +1749,83 @@ void NumberFormatterApiTest::errors() { 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); + "Should fail since rounder is not legal", + (UBool) TRUE, + (UBool) U_FAILURE(status1)); FieldPosition fp; fn.populateFieldPosition(fp, status2); assertEquals( - "Should fail with U_ILLEGAL_ARGUMENT_ERROR on terminal method", - U_ILLEGAL_ARGUMENT_ERROR, - status2); + "Should fail on terminal method", + (UBool) TRUE, + (UBool) U_FAILURE(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); + "Should fail since rounder is not legal", + (UBool) TRUE, + (UBool) U_FAILURE(status)); } } +void NumberFormatterApiTest::validRanges() { + +#define EXPECTED_MAX_INT_FRAC_SIG 999 + +#define VALID_RANGE_ASSERT(status, method, lowerBound, argument) { \ + UErrorCode expectedStatus = ((lowerBound <= argument) && (argument <= EXPECTED_MAX_INT_FRAC_SIG)) \ + ? U_ZERO_ERROR \ + : U_NUMBER_ARG_OUTOFBOUNDS_ERROR; \ + assertEquals( \ + UnicodeString(u"Incorrect status for " #method " on input ") \ + + Int64ToUnicodeString(argument), \ + expectedStatus, \ + status); \ +} + +#define VALID_RANGE_ONEARG(setting, method, lowerBound) { \ + for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \ + UErrorCode status = U_ZERO_ERROR; \ + NumberFormatter::with().setting(method(argument)).copyErrorTo(status); \ + VALID_RANGE_ASSERT(status, method, lowerBound, argument); \ + } \ +} + +#define VALID_RANGE_TWOARGS(setting, method, lowerBound) { \ + for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \ + UErrorCode status = U_ZERO_ERROR; \ + /* Pass EXPECTED_MAX_INT_FRAC_SIG as the second argument so arg1 <= arg2 in expected cases */ \ + NumberFormatter::with().setting(method(argument, EXPECTED_MAX_INT_FRAC_SIG)).copyErrorTo(status); \ + VALID_RANGE_ASSERT(status, method, lowerBound, argument); \ + status = U_ZERO_ERROR; \ + /* Pass lowerBound as the first argument so arg1 <= arg2 in expected cases */ \ + NumberFormatter::with().setting(method(lowerBound, argument)).copyErrorTo(status); \ + VALID_RANGE_ASSERT(status, method, lowerBound, argument); \ + /* Check that first argument must be less than or equal to second argument */ \ + NumberFormatter::with().setting(method(argument, argument - 1)).copyErrorTo(status); \ + assertEquals("Incorrect status for " #method " on max < min input", \ + U_NUMBER_ARG_OUTOFBOUNDS_ERROR, \ + status); \ + } \ +} + + VALID_RANGE_ONEARG(rounding, Rounder::fixedFraction, 0); + VALID_RANGE_ONEARG(rounding, Rounder::minFraction, 0); + VALID_RANGE_ONEARG(rounding, Rounder::maxFraction, 0); + VALID_RANGE_TWOARGS(rounding, Rounder::minMaxFraction, 0); + VALID_RANGE_ONEARG(rounding, Rounder::fixedDigits, 1); + VALID_RANGE_ONEARG(rounding, Rounder::minDigits, 1); + VALID_RANGE_ONEARG(rounding, Rounder::maxDigits, 1); + VALID_RANGE_TWOARGS(rounding, Rounder::minMaxDigits, 1); + VALID_RANGE_ONEARG(rounding, Rounder::fixedFraction(1).withMinDigits, 1); + VALID_RANGE_ONEARG(rounding, Rounder::fixedFraction(1).withMaxDigits, 1); + VALID_RANGE_ONEARG(notation, Notation::scientific().withMinExponentDigits, 1); + VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo, 0); + VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo(0).truncateAt, -1); +} + void NumberFormatterApiTest::assertFormatDescending(const UnicodeString &message, const UnlocalizedNumberFormatter &f, diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/RoundingUtils.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/RoundingUtils.java index 9098d8c6aa2..b9a3cdb6da1 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/RoundingUtils.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/RoundingUtils.java @@ -22,7 +22,7 @@ public class RoundingUtils { * The maximum number of fraction places, integer numerals, or significant digits. TODO: This does * not feel like the best home for this value. */ - public static final int MAX_INT_FRAC_SIG = 100; + public static final int MAX_INT_FRAC_SIG = 999; /** * Converts a rounding mode and metadata about the quantity being rounded to a boolean determining 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 3f9969ad6f7..eb9cb5f2a28 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 @@ -1897,12 +1897,12 @@ public class NumberFormatterApiTest { Rounder.class.getDeclaredMethod("minMaxFraction", Integer.TYPE, Integer.TYPE), Rounder.class.getDeclaredMethod("minMaxDigits", Integer.TYPE, Integer.TYPE), }; - final int EXPECTED_MAX_INT_FRAC_SIG = 100; - final String expectedSubstring0 = "between 0 and 100 (inclusive)"; - final String expectedSubstring1 = "between 1 and 100 (inclusive)"; - final String expectedSubstringN1 = "between -1 and 100 (inclusive)"; + final int EXPECTED_MAX_INT_FRAC_SIG = 999; + final String expectedSubstring0 = "between 0 and 999 (inclusive)"; + final String expectedSubstring1 = "between 1 and 999 (inclusive)"; + final String expectedSubstringN1 = "between -1 and 999 (inclusive)"; - // We require that the upper bounds all be 100 inclusive. + // We require that the upper bounds all be 999 inclusive. // The lower bound may be either -1, 0, or 1. Set methodsWithLowerBound1 = new HashSet(); methodsWithLowerBound1.add("fixedDigits"); @@ -1912,6 +1912,12 @@ public class NumberFormatterApiTest { methodsWithLowerBound1.add("withMinDigits"); methodsWithLowerBound1.add("withMaxDigits"); methodsWithLowerBound1.add("withMinExponentDigits"); + // Methods with lower bound 0: + // fixedFraction + // minFraction + // maxFraction + // minMaxFraction + // zeroFillTo Set methodsWithLowerBoundN1 = new HashSet(); methodsWithLowerBoundN1.add("truncateAt");