diff --git a/icu4c/source/i18n/number_decimalquantity.cpp b/icu4c/source/i18n/number_decimalquantity.cpp index 3c3a77e0fd6..b78e8314668 100644 --- a/icu4c/source/i18n/number_decimalquantity.cpp +++ b/icu4c/source/i18n/number_decimalquantity.cpp @@ -387,9 +387,10 @@ void DecimalQuantity::convertToAccurateDouble() { // Call the slow oracle function (Double.toString in Java, sprintf in C++). // The constant DBL_DIG defines a platform-specific number of digits in a double. - // However, this tends to be too low (see #11318). Instead, we always use 14 digits. - char dstr[14 + 8]; // Extra space for '+', '.', e+NNN, and '\0' - sprintf(dstr, "%+1.14e", n); + // However, this tends to be too low (see #11318). Instead, we always use 14 decimal places. + static constexpr size_t CAP = 1 + 14 + 8; // Extra space for '+', '.', e+NNN, and '\0' + char dstr[CAP]; + snprintf(dstr, CAP, "%+1.14e", n); // uprv_decNumberFromString() will parse the string expecting '.' as a // decimal separator, however sprintf() can use ',' in certain locales. @@ -400,7 +401,8 @@ void DecimalQuantity::convertToAccurateDouble() { } decNumber dn; - stringToDecNumber(dstr, dn); + StringPiece sp(dstr); + stringToDecNumber(sp, dn); _setToDecNumber(&dn); scale += delta; diff --git a/icu4c/source/test/intltest/intltest.cpp b/icu4c/source/test/intltest/intltest.cpp index 8147bbfdfa9..b66e6256761 100644 --- a/icu4c/source/test/intltest/intltest.cpp +++ b/icu4c/source/test/intltest/intltest.cpp @@ -109,6 +109,18 @@ Int64ToUnicodeString(int64_t num) return buffer; } +UnicodeString +DoubleToUnicodeString(double num) +{ + char buffer[64]; // nos changed from 10 to 64 + char danger = 'p'; // guard against overrunning the buffer (rtg) + + sprintf(buffer, "%1.14e", num); + assert(danger == 'p'); + + return buffer; +} + // [LIU] Just to get things working UnicodeString operator+(const UnicodeString& left, diff --git a/icu4c/source/test/intltest/intltest.h b/icu4c/source/test/intltest/intltest.h index 389b313807f..1f7c80d4794 100644 --- a/icu4c/source/test/intltest/intltest.h +++ b/icu4c/source/test/intltest/intltest.h @@ -30,6 +30,7 @@ U_NAMESPACE_USE //string-concatenation operator (moved from findword test by rtg) UnicodeString UCharToUnicodeString(UChar c); UnicodeString Int64ToUnicodeString(int64_t num); +UnicodeString DoubleToUnicodeString(double num); //UnicodeString operator+(const UnicodeString& left, int64_t num); // Some compilers don't allow this because of the long type. UnicodeString operator+(const UnicodeString& left, long num); UnicodeString operator+(const UnicodeString& left, unsigned long num); diff --git a/icu4c/source/test/intltest/numbertest.h b/icu4c/source/test/intltest/numbertest.h index ec07d9784fd..1b8caa04aea 100644 --- a/icu4c/source/test/intltest/numbertest.h +++ b/icu4c/source/test/intltest/numbertest.h @@ -58,6 +58,7 @@ class NumberFormatterApiTest : public IntlTest { void sign(); void decimal(); void locale(); + void formatTypes(); void errors(); void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0); @@ -101,7 +102,7 @@ class DecimalQuantityTest : public IntlTest { void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0); private: - void assertDoubleEquals(const char *message, double a, double b); + void assertDoubleEquals(UnicodeString message, double a, double b); void assertHealth(const DecimalQuantity &fq); void assertToStringAndHealth(const DecimalQuantity &fq, const UnicodeString &expected); void checkDoubleBehavior(double d, bool explicitRequired); diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp index 7ada621824e..ba0f6269d58 100644 --- a/icu4c/source/test/intltest/numbertest_api.cpp +++ b/icu4c/source/test/intltest/numbertest_api.cpp @@ -70,6 +70,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha TESTCASE_AUTO(sign); TESTCASE_AUTO(decimal); TESTCASE_AUTO(locale); + TESTCASE_AUTO(formatTypes); TESTCASE_AUTO(errors); TESTCASE_AUTO_END; } @@ -1402,6 +1403,14 @@ void NumberFormatterApiTest::locale() { 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( diff --git a/icu4c/source/test/intltest/numbertest_decimalquantity.cpp b/icu4c/source/test/intltest/numbertest_decimalquantity.cpp index d863c587489..136d90c7a1a 100644 --- a/icu4c/source/test/intltest/numbertest_decimalquantity.cpp +++ b/icu4c/source/test/intltest/numbertest_decimalquantity.cpp @@ -21,7 +21,7 @@ void DecimalQuantityTest::runIndexedTest(int32_t index, UBool exec, const char * TESTCASE_AUTO_END; } -void DecimalQuantityTest::assertDoubleEquals(const char *message, double a, double b) { +void DecimalQuantityTest::assertDoubleEquals(UnicodeString message, double a, double b) { if (a == b) { return; } @@ -30,7 +30,7 @@ void DecimalQuantityTest::assertDoubleEquals(const char *message, double a, doub diff = diff < 0 ? -diff : diff; double bound = a < 0 ? -a * 1e-6 : a * 1e-6; if (diff > bound) { - errln(message); + errln(message + u": " + DoubleToUnicodeString(a) + u" vs " + DoubleToUnicodeString(b) + u" differ by " + DoubleToUnicodeString(diff)); } } @@ -54,12 +54,18 @@ void DecimalQuantityTest::checkDoubleBehavior(double d, bool explicitRequired) { if (explicitRequired) { assertTrue("Should be using approximate double", !fq.isExplicitExactDouble()); } - assertDoubleEquals("Initial construction from hard double", d, fq.toDouble()); + UnicodeString baseStr = fq.toString(); + assertDoubleEquals( + UnicodeString(u"Initial construction from hard double: ") + baseStr, + d, fq.toDouble()); fq.roundToInfinity(); + UnicodeString newStr = fq.toString(); if (explicitRequired) { assertTrue("Should not be using approximate double", fq.isExplicitExactDouble()); } - assertDoubleEquals("After conversion to exact BCD (double)", d, fq.toDouble()); + assertDoubleEquals( + UnicodeString(u"After conversion to exact BCD (double): ") + baseStr + u" vs " + newStr, + d, fq.toDouble()); } void DecimalQuantityTest::testDecimalQuantityBehaviorStandalone() { @@ -205,11 +211,11 @@ void DecimalQuantityTest::testConvertToAccurateDouble() { checkDoubleBehavior(d, false); } - assertDoubleEquals("NaN check failed", NAN, DecimalQuantity().setToDouble(NAN).toDouble()); + assertDoubleEquals(u"NaN check failed", NAN, DecimalQuantity().setToDouble(NAN).toDouble()); assertDoubleEquals( - "Inf check failed", INFINITY, DecimalQuantity().setToDouble(INFINITY).toDouble()); + u"Inf check failed", INFINITY, DecimalQuantity().setToDouble(INFINITY).toDouble()); assertDoubleEquals( - "-Inf check failed", -INFINITY, DecimalQuantity().setToDouble(-INFINITY).toDouble()); + u"-Inf check failed", -INFINITY, DecimalQuantity().setToDouble(-INFINITY).toDouble()); // Generate random doubles for (int32_t i = 0; i < 10000; i++) {