1 // Copyright (C) 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /*********************************************************************** 4 * COPYRIGHT: 5 * Copyright (c) 1997-2015, International Business Machines Corporation 6 * and others. All Rights Reserved. 7 ***********************************************************************/ 8 9 #include "unicode/utypes.h" 10 11 #if !UCONFIG_NO_FORMATTING 12 13 #include "nmfmtrt.h" 14 15 #include "unicode/dcfmtsym.h" 16 #include "unicode/decimfmt.h" 17 #include "unicode/locid.h" 18 #include "putilimp.h" 19 20 #include <float.h> 21 #include <stdio.h> // for sprintf 22 #include <stdlib.h> 23 24 // ***************************************************************************** 25 // class NumberFormatRoundTripTest 26 // ***************************************************************************** 27 28 UBool NumberFormatRoundTripTest::verbose = FALSE; 29 UBool NumberFormatRoundTripTest::STRING_COMPARE = TRUE; 30 UBool NumberFormatRoundTripTest::EXACT_NUMERIC_COMPARE = FALSE; 31 UBool NumberFormatRoundTripTest::DEBUG_VAR = FALSE; 32 double NumberFormatRoundTripTest::MAX_ERROR = 1e-14; 33 double NumberFormatRoundTripTest::max_numeric_error = 0.0; 34 double NumberFormatRoundTripTest::min_numeric_error = 1.0; 35 36 #define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break; 37 38 void NumberFormatRoundTripTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) 39 { 40 // if (exec) logln((UnicodeString)"TestSuite NumberFormatRoundTripTest"); 41 switch (index) { 42 CASE(0, start) 43 default: name = ""; break; 44 } 45 } 46 47 UBool 48 NumberFormatRoundTripTest::failure(UErrorCode status, const char* msg, UBool possibleDataError) 49 { 50 if(U_FAILURE(status)) { 51 if (possibleDataError) { 52 dataerrln(UnicodeString("FAIL: ") + msg + " failed, error " + u_errorName(status)); 53 } else { 54 errln(UnicodeString("FAIL: ") + msg + " failed, error " + u_errorName(status)); 55 } 56 return TRUE; 57 } 58 59 return FALSE; 60 } 61 62 uint32_t 63 NumberFormatRoundTripTest::randLong() 64 { 65 // Assume 8-bit (or larger) rand values. Also assume 66 // that the system rand() function is very poor, which it always is. 67 uint32_t d; 68 uint32_t i; 69 char* poke = (char*)&d; 70 for (i=0; i < sizeof(uint32_t); ++i) 71 { 72 poke[i] = (char)(rand() & 0xFF); 73 } 74 return d; 75 } 76 77 /** 78 * Return a random value from -range..+range. 79 */ 80 double 81 NumberFormatRoundTripTest::randomDouble(double range) 82 { 83 double a = randFraction(); 84 return (2.0 * range * a) - range; 85 } 86 87 void 88 NumberFormatRoundTripTest::start() 89 { 90 // test(NumberFormat.getInstance(new Locale("sr", "", ""))); 91 92 UErrorCode status = U_ZERO_ERROR; 93 94 NumberFormat *fmt = NULL; 95 96 logln("Default Locale"); 97 98 fmt = NumberFormat::createInstance(status); 99 if (!failure(status, "NumberFormat::createInstance", TRUE)){ 100 test(fmt); 101 } 102 delete fmt; 103 104 fmt = NumberFormat::createCurrencyInstance(status); 105 if (!failure(status, "NumberFormat::createCurrencyInstance", TRUE)){ 106 test(fmt); 107 } 108 delete fmt; 109 110 fmt = NumberFormat::createPercentInstance(status); 111 if (!failure(status, "NumberFormat::createPercentInstance", TRUE)){ 112 test(fmt); 113 } 114 delete fmt; 115 116 117 int32_t locCount = 0; 118 const Locale *loc = NumberFormat::getAvailableLocales(locCount); 119 if(quick) { 120 if(locCount > 5) 121 locCount = 5; 122 logln("Quick mode: only testing first 5 Locales"); 123 } 124 for(int i = 0; i < locCount; ++i) { 125 UnicodeString name; 126 logln(loc[i].getDisplayName(name)); 127 128 fmt = NumberFormat::createInstance(loc[i], status); 129 failure(status, "NumberFormat::createInstance"); 130 test(fmt); 131 delete fmt; 132 133 fmt = NumberFormat::createCurrencyInstance(loc[i], status); 134 failure(status, "NumberFormat::createCurrencyInstance"); 135 test(fmt); 136 delete fmt; 137 138 fmt = NumberFormat::createPercentInstance(loc[i], status); 139 failure(status, "NumberFormat::createPercentInstance"); 140 test(fmt); 141 delete fmt; 142 } 143 144 logln(UnicodeString("Numeric error ") + min_numeric_error + " to " + max_numeric_error); 145 } 146 147 148 void 149 NumberFormatRoundTripTest::test(NumberFormat *fmt) 150 { 151 #if IEEE_754 && U_PLATFORM != U_PF_OS400 152 test(fmt, uprv_getNaN()); 153 test(fmt, uprv_getInfinity()); 154 test(fmt, -uprv_getInfinity()); 155 #endif 156 157 test(fmt, (int32_t)500); 158 test(fmt, (int32_t)0); 159 test(fmt, (int32_t)-0); 160 test(fmt, 0.0); 161 double negZero = 0.0; negZero /= -1.0; 162 test(fmt, negZero); 163 test(fmt, 9223372036854775808.0); 164 test(fmt, -9223372036854775809.0); 165 166 for(int i = 0; i < 10; ++i) { 167 test(fmt, randomDouble(1)); 168 test(fmt, randomDouble(10000)); 169 test(fmt, uprv_floor((randomDouble(10000)))); 170 test(fmt, randomDouble(1e50)); 171 test(fmt, randomDouble(1e-50)); 172 #if !(U_PF_OS390 <= U_PLATFORM && U_PLATFORM <= U_PF_OS400) 173 test(fmt, randomDouble(1e100)); 174 #elif IEEE_754 175 test(fmt, randomDouble(1e75)); 176 #endif /* OS390 and OS400 */ 177 // {sfb} When formatting with a percent instance, numbers very close to 178 // DBL_MAX will fail the round trip. This is because: 179 // 1) Format the double into a string --> INF% (since 100 * double > DBL_MAX) 180 // 2) Parse the string into a double --> INF 181 // 3) Re-format the double --> INF% 182 // 4) The strings are equal, so that works. 183 // 5) Calculate the proportional error --> INF, so the test will fail 184 // I'll get around this by dividing by the multiplier to make sure 185 // the double will stay in range. 186 //if(fmt->getMultipler() == 1) 187 DecimalFormat *df = dynamic_cast<DecimalFormat *>(fmt); 188 if(df != NULL) 189 { 190 #if !(U_PF_OS390 <= U_PLATFORM && U_PLATFORM <= U_PF_OS400) 191 /* DBL_MAX/2 is here because randomDouble does a *2 in the math */ 192 test(fmt, randomDouble(DBL_MAX/2.0) / df->getMultiplier()); 193 #elif IEEE_754 194 test(fmt, randomDouble(1e75) / df->getMultiplier()); 195 #else 196 test(fmt, randomDouble(1e65) / df->getMultiplier()); 197 #endif 198 } 199 200 #if (defined(_MSC_VER) && _MSC_VER < 1400) || defined(__alpha__) || defined(U_OSF) 201 // These machines and compilers don't fully support denormalized doubles, 202 test(fmt, randomDouble(1e-292)); 203 test(fmt, randomDouble(1e-100)); 204 #elif U_PF_OS390 <= U_PLATFORM && U_PLATFORM <= U_PF_OS400 205 // i5/OS (OS/400) throws exceptions on denormalized numbers 206 # if IEEE_754 207 test(fmt, randomDouble(1e-78)); 208 test(fmt, randomDouble(1e-78)); 209 // #else we're using something like the old z/OS floating point. 210 # endif 211 #else 212 // This is a normal machine that can support IEEE754 denormalized doubles without throwing an error. 213 test(fmt, randomDouble(DBL_MIN)); /* Usually 2.2250738585072014e-308 */ 214 test(fmt, randomDouble(1e-100)); 215 #endif 216 } 217 } 218 219 void 220 NumberFormatRoundTripTest::test(NumberFormat *fmt, double value) 221 { 222 test(fmt, Formattable(value)); 223 } 224 225 void 226 NumberFormatRoundTripTest::test(NumberFormat *fmt, int32_t value) 227 { 228 test(fmt, Formattable(value)); 229 } 230 231 void 232 NumberFormatRoundTripTest::test(NumberFormat *fmt, const Formattable& value) 233 { 234 fmt->setMaximumFractionDigits(999); 235 DecimalFormat *df = dynamic_cast<DecimalFormat *>(fmt); 236 if(df != NULL) { 237 df->setRoundingIncrement(0.0); 238 } 239 UErrorCode status = U_ZERO_ERROR; 240 UnicodeString s, s2, temp; 241 if(isDouble(value)) 242 s = fmt->format(value.getDouble(), s); 243 else 244 s = fmt->format(value.getLong(), s); 245 246 Formattable n; 247 UBool show = verbose; 248 if(DEBUG_VAR) 249 logln(/*value.getString(temp) +*/ " F> " + escape(s)); 250 251 fmt->parse(s, n, status); 252 failure(status, "fmt->parse"); 253 if(DEBUG_VAR) 254 logln(escape(s) + " P> " /*+ n.getString(temp)*/); 255 256 if(isDouble(n)) 257 s2 = fmt->format(n.getDouble(), s2); 258 else 259 s2 = fmt->format(n.getLong(), s2); 260 261 if(DEBUG_VAR) 262 logln(/*n.getString(temp) +*/ " F> " + escape(s2)); 263 264 if(STRING_COMPARE) { 265 if (s != s2) { 266 errln("*** STRING ERROR \"" + escape(s) + "\" != \"" + escape(s2) + "\""); 267 show = TRUE; 268 } 269 } 270 271 if(EXACT_NUMERIC_COMPARE) { 272 if(value != n) { 273 errln("*** NUMERIC ERROR"); 274 show = TRUE; 275 } 276 } 277 else { 278 // Compute proportional error 279 double error = proportionalError(value, n); 280 281 if(error > MAX_ERROR) { 282 errln(UnicodeString("*** NUMERIC ERROR ") + error); 283 show = TRUE; 284 } 285 286 if (error > max_numeric_error) 287 max_numeric_error = error; 288 if (error < min_numeric_error) 289 min_numeric_error = error; 290 } 291 292 if (show) { 293 errln(/*value.getString(temp) +*/ typeOf(value, temp) + " F> " + 294 escape(s) + " P> " + (n.getType() == Formattable::kDouble ? n.getDouble() : (double)n.getLong()) 295 /*n.getString(temp) */ + typeOf(n, temp) + " F> " + 296 escape(s2)); 297 } 298 } 299 300 double 301 NumberFormatRoundTripTest::proportionalError(const Formattable& a, const Formattable& b) 302 { 303 double aa,bb; 304 305 if(isDouble(a)) 306 aa = a.getDouble(); 307 else 308 aa = a.getLong(); 309 310 if(isDouble(b)) 311 bb = b.getDouble(); 312 else 313 bb = b.getLong(); 314 315 double error = aa - bb; 316 if(aa != 0 && bb != 0) 317 error /= aa; 318 319 return uprv_fabs(error); 320 } 321 322 UnicodeString& 323 NumberFormatRoundTripTest::typeOf(const Formattable& n, UnicodeString& result) 324 { 325 if(n.getType() == Formattable::kLong) { 326 result = UnicodeString(" Long"); 327 } 328 else if(n.getType() == Formattable::kDouble) { 329 result = UnicodeString(" Double"); 330 } 331 else if(n.getType() == Formattable::kString) { 332 result = UnicodeString(" UnicodeString"); 333 UnicodeString temp; 334 } 335 336 return result; 337 } 338 339 340 UnicodeString& 341 NumberFormatRoundTripTest::escape(UnicodeString& s) 342 { 343 UnicodeString copy(s); 344 s.remove(); 345 for(int i = 0; i < copy.length(); ++i) { 346 UChar c = copy[i]; 347 if(c < 0x00FF) 348 s += c; 349 else { 350 s += "+U"; 351 char temp[16]; 352 sprintf(temp, "%4X", c); // might not work 353 s += temp; 354 } 355 } 356 return s; 357 } 358 359 #endif /* #if !UCONFIG_NO_FORMATTING */ 360