1/*********************************************************************** 2 * COPYRIGHT: 3 * Copyright (c) 1997-2010, 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 26UBool NumberFormatRoundTripTest::verbose = FALSE; 27UBool NumberFormatRoundTripTest::STRING_COMPARE = TRUE; 28UBool NumberFormatRoundTripTest::EXACT_NUMERIC_COMPARE = FALSE; 29UBool NumberFormatRoundTripTest::DEBUG = FALSE; 30double NumberFormatRoundTripTest::MAX_ERROR = 1e-14; 31double NumberFormatRoundTripTest::max_numeric_error = 0.0; 32double 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 36void 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 45UBool 46NumberFormatRoundTripTest::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 60uint32_t 61NumberFormatRoundTripTest::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 */ 78double 79NumberFormatRoundTripTest::randomDouble(double range) 80{ 81 double a = randFraction(); 82 return (2.0 * range * a) - range; 83} 84 85void 86NumberFormatRoundTripTest::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 146void 147NumberFormatRoundTripTest::test(NumberFormat *fmt) 148{ 149#if IEEE_754 && !defined(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 !defined(OS390) && !defined(OS400) 171 test(fmt, randomDouble(1e100)); 172#elif IEEE_754 173 test(fmt, randomDouble(1e75)); /*OS390 and OS400*/ 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 !defined(OS390) && !defined(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()); /*OS390*/ 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 defined(OS390) || defined(OS400) 203 // i5/OS (OS400) 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 217void 218NumberFormatRoundTripTest::test(NumberFormat *fmt, double value) 219{ 220 test(fmt, Formattable(value)); 221} 222 223void 224NumberFormatRoundTripTest::test(NumberFormat *fmt, int32_t value) 225{ 226 test(fmt, Formattable(value)); 227} 228 229void 230NumberFormatRoundTripTest::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 298double 299NumberFormatRoundTripTest::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 320UnicodeString& 321NumberFormatRoundTripTest::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 338UnicodeString& 339NumberFormatRoundTripTest::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