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