+//---------------------------------------------------------------------------
+//
+// Error Checking / Reporting macros
+//
+//---------------------------------------------------------------------------
+#define TEST_CHECK_STATUS(status) { \
+ if (U_FAILURE(status)) { \
+ if (status == U_MISSING_RESOURCE_ERROR) { \
+ dataerrln("File %s, Line %d: status=%s", __FILE__, __LINE__, u_errorName(status)); \
+ } else { \
+ errln("File %s, Line %d: status=%s", __FILE__, __LINE__, u_errorName(status)); \
+ } return; \
+ }}
+
+#define TEST_ASSERT(expr) \
+ if ((expr)==FALSE) {\
+ errln("File %s, line %d: Assertion Failed: " #expr "\n", __FILE__, __LINE__);\
+ }
+
+
+// Ticket 8199: Parse failure for numbers in the range of 1E10 - 1E18
+
+void NumberFormatRegressionTest::Test8199(void) {
+ UErrorCode status = U_ZERO_ERROR;
+ NumberFormat *nf = NumberFormat::createInstance(Locale::getEnglish(), status);
+ if (nf == NULL) {
+ dataerrln("Fail: NumberFormat::createInstance(Locale::getEnglish(), status)");
+ return;
+ }
+ TEST_CHECK_STATUS(status);
+
+ // Note: Retrieving parsed values from a Formattable as a reduced-precision type
+ // should always truncate, no other rounding scheme.
+
+ UnicodeString numStr = "1000000000.6"; // 9 zeroes
+ Formattable val;
+ nf->parse(numStr, val, status);
+ TEST_CHECK_STATUS(status);
+ TEST_ASSERT(Formattable::kDouble == val.getType());
+ TEST_ASSERT(1000000000 == val.getInt64(status));
+ TEST_CHECK_STATUS(status);
+ TEST_ASSERT(1000000000.6 == val.getDouble(status));
+ TEST_CHECK_STATUS(status);
+
+ numStr = "100000000000000001.1"; // approx 1E17, parses as a double rather
+ // than int64 because of the fraction
+ // even though int64 is more precise.
+ nf->parse(numStr, val, status);
+ TEST_CHECK_STATUS(status);
+ TEST_ASSERT(Formattable::kDouble == val.getType());
+ TEST_ASSERT(100000000000000001LL == val.getInt64(status));
+ TEST_CHECK_STATUS(status);
+ TEST_ASSERT(100000000000000000.0 == val.getDouble(status));
+ TEST_CHECK_STATUS(status);
+
+ numStr = "1E17"; // Parses with the internal decimal number having non-zero exponent
+ nf->parse(numStr, val, status);
+ TEST_CHECK_STATUS(status);
+ TEST_ASSERT(Formattable::kInt64 == val.getType());
+ TEST_ASSERT(100000000000000000LL == val.getInt64());
+ TEST_ASSERT(1.0E17 == val.getDouble(status));
+ TEST_CHECK_STATUS(status);
+
+ numStr = "9223372036854775807"; // largest int64_t
+ nf->parse(numStr, val, status);
+ TEST_CHECK_STATUS(status);
+ TEST_ASSERT(Formattable::kInt64 == val.getType());
+ TEST_ASSERT(9223372036854775807LL == val.getInt64());
+ // In the following check, note that a substantial range of integers will
+ // convert to the same double value. There are also platform variations
+ // in the rounding at compile time of double constants.
+ TEST_ASSERT(9223372036854775808.0 >= val.getDouble(status));
+ TEST_ASSERT(9223372036854774700.0 <= val.getDouble(status));
+ TEST_CHECK_STATUS(status);
+
+ numStr = "-9223372036854775808"; // smallest int64_t
+ nf->parse(numStr, val, status);
+ TEST_CHECK_STATUS(status);
+ TEST_ASSERT(Formattable::kInt64 == val.getType());
+ // TEST_ASSERT(-9223372036854775808LL == val.getInt64()); // Compiler chokes on constant.
+ TEST_ASSERT((int64_t)0x8000000000000000LL == val.getInt64());
+ TEST_ASSERT(-9223372036854775808.0 == val.getDouble(status));
+ TEST_CHECK_STATUS(status);
+
+ numStr = "9223372036854775808"; // largest int64_t + 1
+ nf->parse(numStr, val, status);
+ TEST_CHECK_STATUS(status);
+ TEST_ASSERT(Formattable::kDouble == val.getType());
+ TEST_ASSERT(9223372036854775807LL == val.getInt64(status));
+ TEST_ASSERT(status == U_INVALID_FORMAT_ERROR);
+ status = U_ZERO_ERROR;
+ TEST_ASSERT(9223372036854775810.0 == val.getDouble(status));
+ TEST_CHECK_STATUS(status);
+
+ numStr = "-9223372036854775809"; // smallest int64_t - 1
+ nf->parse(numStr, val, status);
+ TEST_CHECK_STATUS(status);
+ TEST_ASSERT(Formattable::kDouble == val.getType());
+ // TEST_ASSERT(-9223372036854775808LL == val.getInt64(status)); // spurious compiler warnings
+ TEST_ASSERT((int64_t)0x8000000000000000LL == val.getInt64(status));
+ TEST_ASSERT(status == U_INVALID_FORMAT_ERROR);
+ status = U_ZERO_ERROR;
+ TEST_ASSERT(-9223372036854775810.0 == val.getDouble(status));
+ TEST_CHECK_STATUS(status);
+
+ // Test values near the limit of where doubles can represent all integers.
+ // The implementation strategy of getInt64() changes at this boundary.
+ // Strings to be parsed include a decimal fraction to force them to be
+ // parsed as doubles rather than ints. The fraction is discarded
+ // from the parsed double value because it is beyond what can be represented.
+
+ status = U_ZERO_ERROR;
+ numStr = "9007199254740991.1"; // largest 53 bit int
+ nf->parse(numStr, val, status);
+ TEST_CHECK_STATUS(status);
+ // printf("getInt64() returns %lld\n", val.getInt64(status));
+ TEST_ASSERT(Formattable::kDouble == val.getType());
+ TEST_ASSERT(9007199254740991LL == val.getInt64(status));
+ TEST_ASSERT(9007199254740991.0 == val.getDouble(status));
+ TEST_CHECK_STATUS(status);
+
+ status = U_ZERO_ERROR;
+ numStr = "9007199254740992.1"; // 54 bits for the int part.
+ nf->parse(numStr, val, status);
+ TEST_CHECK_STATUS(status);
+ TEST_ASSERT(Formattable::kDouble == val.getType());
+ TEST_ASSERT(9007199254740992LL == val.getInt64(status));
+ TEST_ASSERT(9007199254740992.0 == val.getDouble(status));
+ TEST_CHECK_STATUS(status);
+
+ status = U_ZERO_ERROR;
+ numStr = "9007199254740993.1"; // 54 bits for the int part. Double will round
+ nf->parse(numStr, val, status); // the ones digit, putting it up to ...994
+ TEST_CHECK_STATUS(status);
+ TEST_ASSERT(Formattable::kDouble == val.getType());
+ TEST_ASSERT(9007199254740993LL == val.getInt64(status));
+ TEST_ASSERT(9007199254740994.0 == val.getDouble(status));
+ TEST_CHECK_STATUS(status);
+
+ delete nf;
+}
+
+void NumberFormatRegressionTest::Test9109(void) {
+ UErrorCode status = U_ZERO_ERROR;
+ Formattable val;
+ ParsePosition pos;
+ DecimalFormat fmt("+##", status);
+ fmt.setLenient(TRUE);
+
+ if (U_FAILURE(status)) {
+ dataerrln("Failed to create DecimalFormat with pattern '+##' - %s", u_errorName(status));
+ }
+
+ UnicodeString text("123");
+ int32_t expected = 123;
+ int32_t expos = 3;
+
+ fmt.parse(text, val, pos);
+ if (pos.getErrorIndex() >= 0) {
+ errln(UnicodeString("Parse failure at ") + pos.getErrorIndex());
+ } else if (val.getLong() != 123) {
+ errln(UnicodeString("Incorrect parse result: ") + val.getLong() + " expected: " + expected);
+ } else if (pos.getIndex() != 3) {
+ errln(UnicodeString("Incorrect parse position: ") + pos.getIndex() + " expected: " + expos);
+ }
+}
+
+
+void NumberFormatRegressionTest::Test9780(void) {
+ UErrorCode status = U_ZERO_ERROR;
+ NumberFormat *nf = NumberFormat::createInstance(Locale::getUS(), status);
+ if (failure(status, "NumberFormat::createInstance", TRUE)){
+ delete nf;
+ return;
+ };
+ DecimalFormat *df = dynamic_cast<DecimalFormat *>(nf);
+ if(df == NULL) {
+ errln("DecimalFormat needed to continue");
+ return;
+ }
+ df->setParseIntegerOnly(TRUE);
+
+ {
+ Formattable n;
+ ParsePosition pos(0);
+ UnicodeString toParse("1,234","");
+ df->parse(toParse, n, pos);
+ if (n.getType() != Formattable::kLong
+ || n.getLong() != 1234) {
+ errln(UnicodeString("FAIL: parse(\"") + toParse + UnicodeString("\") returns ") + toString(n));
+ }
+ }
+ // should still work in lenient mode, just won't get fastpath
+ df->setLenient(TRUE);
+ {
+ Formattable n;
+ ParsePosition pos(0);
+ UnicodeString toParse("1,234","");
+ df->parse(toParse, n, pos);
+ if (n.getType() != Formattable::kLong
+ || n.getLong() != 1234) {
+ errln(UnicodeString("FAIL: parse(\"") + toParse + UnicodeString("\") returns ") + toString(n));
+ }
+ }
+ delete nf;
+}
+
+
+void NumberFormatRegressionTest::Test9677(void) {
+ static const UChar pattern[] = { 0x23,0x23,0x23,0x23,0x2E,0x23,0x23,0x23,0x23,0 }; // "####.####"
+ static const UChar positivePrefix[] = { 0x40,0 }; // "@"
+ static const UChar negativePrefix[] = { 0x6E,0 }; // "n"
+ static const UChar text[] = { 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0 }; // 123456789
+ static const UChar text2[] = { 0x6E, 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0 }; // n123456789
+
+ UErrorCode status = U_ZERO_ERROR;
+ LocalUNumberFormatPointer f(unum_open(UNUM_DEFAULT, NULL, 0, "en_US", NULL, &status));
+ if (U_FAILURE(status)) {
+ dataerrln("Failure opening unum_open");
+ return;
+ }
+
+ if (U_SUCCESS(status)) {
+ unum_applyPattern(f.getAlias(), FALSE, pattern, -1, NULL, &status);
+ unum_setTextAttribute(f.getAlias(), UNUM_POSITIVE_PREFIX, positivePrefix, -1, &status);
+ assertSuccess("setting attributes", status);
+ }
+
+ if(U_SUCCESS(status)) {
+ int32_t n = unum_parse(f.getAlias(), text, -1, NULL, &status);
+ logln("unum_parse status %s, result %d\n", u_errorName(status), n);
+
+ if(U_FAILURE(status)) {
+ logln("Got expected parse error %s\n", u_errorName(status));
+ status = U_ZERO_ERROR;
+ } else {
+ errln("FAIL: unum_parse status %s, result %d - expected failure\n", u_errorName(status), n);
+ }
+ }
+
+ if (U_SUCCESS(status)) {
+ unum_setTextAttribute(f.getAlias(), UNUM_POSITIVE_PREFIX, NULL, 0, &status);
+ assertSuccess("setting attributes", status);
+ logln("removed positive prefix");
+ }
+
+ if(U_SUCCESS(status)) {
+ int32_t n = unum_parse(f.getAlias(), text, -1, NULL, &status);
+ logln("unum_parse status %s, result %d\n", u_errorName(status), n);
+
+ if(U_FAILURE(status)) {
+ errln("FAIL: with pos prefix removed, parse error %s\n", u_errorName(status));
+ status = U_ZERO_ERROR;
+ } else {
+ if(n!=123456789) {
+ errln("FAIL: with pos prefix removed , unum_parse status %s, result %d expected 123456789\n", u_errorName(status), n);
+ } else {
+ logln("PASS: with pos prefix removed , unum_parse status %s, result %d expected 123456789\n", u_errorName(status),n);
+ }
+ }
+ }
+
+ if(U_SUCCESS(status)) {
+ int32_t n = unum_parse(f.getAlias(), text2, -1, NULL, &status);
+ logln("unum_parse status %s, result %d\n", u_errorName(status), n);
+
+ if(U_FAILURE(status)) {
+ logln("text2: Got expected parse error %s\n", u_errorName(status));
+ status = U_ZERO_ERROR;
+ } else {
+ errln("FAIL: text2: unum_parse status %s, result %d - expected failure\n", u_errorName(status), n);
+ }
+ }
+
+ if (U_SUCCESS(status)) {
+ unum_setTextAttribute(f.getAlias(), UNUM_NEGATIVE_PREFIX, negativePrefix, -1, &status);
+ assertSuccess("setting attributes", status);
+ logln("Set a different neg prefix prefix");
+ }
+
+ if(U_SUCCESS(status)) {
+ int32_t n = unum_parse(f.getAlias(), text2, -1, NULL, &status);
+ logln("unum_parse status %s, result %d\n", u_errorName(status), n);
+
+ if(U_FAILURE(status)) {
+ errln("FAIL: with different neg prefix , parse error %s\n", u_errorName(status));
+ status = U_ZERO_ERROR;
+ } else {
+;
+ if(n!=-123456789) {
+ errln("FAIL: with different neg prefix , unum_parse status %s, result %d expected -123456789\n", u_errorName(status), n);
+ } else {
+ logln("PASS: with different neg prefix , unum_parse status %s, result %d expected -123456789\n", u_errorName(status), n);
+ }
+ }
+ }
+}
+
+void NumberFormatRegressionTest::Test10361(void) {
+ // DecimalFormat/NumberFormat were artificially limiting the number of digits,
+ // preventing formatting of big decimals.
+ UErrorCode status = U_ZERO_ERROR;
+ DecimalFormatSymbols symbols(Locale::getEnglish(), status);
+ LocalPointer<DecimalFormat> df(new DecimalFormat("###.##", symbols, status), status);
+ TEST_CHECK_STATUS(status);
+
+ // Create a decimal number with a million digits.
+ const int32_t NUMSIZE=1000000;
+ char *num = new char[NUMSIZE];
+ for (int32_t i=0; i<NUMSIZE; i++) {
+ num[i] = '0' + (i+1) % 10;
+ }
+ num[NUMSIZE-3] = '.';
+ num[NUMSIZE-1] = 0;
+
+ UnicodeString s;
+ Formattable fmtable;
+ fmtable.setDecimalNumber(num, status);
+ TEST_CHECK_STATUS(status);
+
+ FieldPosition pos(UNUM_DECIMAL_SEPARATOR_FIELD);
+ df->format(fmtable, s, pos, status);
+ TEST_CHECK_STATUS(status);
+ TEST_ASSERT(999999 == s.length());
+ TEST_ASSERT(999997 == pos.getBeginIndex());
+ TEST_ASSERT(999998 == pos.getEndIndex());
+
+ UnicodeString expected(num, -1, US_INV);
+ TEST_ASSERT(expected == s);
+ delete [] num;
+}
+