1 /*********************************************************************** 2 * COPYRIGHT: 3 * Copyright (c) 1997-2009, 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 && !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 if(fmt->getDynamicClassID() == DecimalFormat::getStaticClassID()) 186 { 187 #if !defined(OS390) && !defined(OS400) 188 /* DBL_MAX/2 is here because randomDouble does a *2 in the math */ 189 test(fmt, randomDouble(DBL_MAX/2.0) / ((DecimalFormat*)fmt)->getMultiplier()); 190 #elif IEEE_754 191 test(fmt, randomDouble(1e75) / ((DecimalFormat*)fmt)->getMultiplier()); 192 #else 193 test(fmt, randomDouble(1e65) / ((DecimalFormat*)fmt)->getMultiplier()); /*OS390*/ 194 #endif 195 } 196 197 #if (defined(_MSC_VER) && _MSC_VER < 1400) || defined(__alpha__) || defined(U_OSF) 198 // These machines and compilers don't fully support denormalized doubles, 199 test(fmt, randomDouble(1e-292)); 200 test(fmt, randomDouble(1e-100)); 201 #elif defined(OS390) || defined(OS400) 202 // i5/OS (OS400) throws exceptions on denormalized numbers 203 # if IEEE_754 204 test(fmt, randomDouble(1e-78)); 205 test(fmt, randomDouble(1e-78)); 206 // #else we're using something like the old z/OS floating point. 207 # endif 208 #else 209 // This is a normal machine that can support IEEE754 denormalized doubles without throwing an error. 210 test(fmt, randomDouble(DBL_MIN)); /* Usually 2.2250738585072014e-308 */ 211 test(fmt, randomDouble(1e-100)); 212 #endif 213 } 214 } 215 216 void 217 NumberFormatRoundTripTest::test(NumberFormat *fmt, double value) 218 { 219 test(fmt, Formattable(value)); 220 } 221 222 void 223 NumberFormatRoundTripTest::test(NumberFormat *fmt, int32_t value) 224 { 225 test(fmt, Formattable(value)); 226 } 227 228 void 229 NumberFormatRoundTripTest::test(NumberFormat *fmt, const Formattable& value) 230 { 231 fmt->setMaximumFractionDigits(999); 232 if(fmt->getDynamicClassID() == DecimalFormat::getStaticClassID()) { 233 ((DecimalFormat *)fmt)->setRoundingIncrement(0.0); 234 } 235 UErrorCode status = U_ZERO_ERROR; 236 UnicodeString s, s2, temp; 237 if(isDouble(value)) 238 s = fmt->format(value.getDouble(), s); 239 else 240 s = fmt->format(value.getLong(), s); 241 242 Formattable n; 243 UBool show = verbose; 244 if(DEBUG) 245 logln(/*value.getString(temp) +*/ " F> " + escape(s)); 246 247 fmt->parse(s, n, status); 248 failure(status, "fmt->parse"); 249 if(DEBUG) 250 logln(escape(s) + " P> " /*+ n.getString(temp)*/); 251 252 if(isDouble(n)) 253 s2 = fmt->format(n.getDouble(), s2); 254 else 255 s2 = fmt->format(n.getLong(), s2); 256 257 if(DEBUG) 258 logln(/*n.getString(temp) +*/ " F> " + escape(s2)); 259 260 if(STRING_COMPARE) { 261 if (s != s2) { 262 errln("*** STRING ERROR \"" + escape(s) + "\" != \"" + escape(s2) + "\""); 263 show = TRUE; 264 } 265 } 266 267 if(EXACT_NUMERIC_COMPARE) { 268 if(value != n) { 269 errln("*** NUMERIC ERROR"); 270 show = TRUE; 271 } 272 } 273 else { 274 // Compute proportional error 275 double error = proportionalError(value, n); 276 277 if(error > MAX_ERROR) { 278 errln(UnicodeString("*** NUMERIC ERROR ") + error); 279 show = TRUE; 280 } 281 282 if (error > max_numeric_error) 283 max_numeric_error = error; 284 if (error < min_numeric_error) 285 min_numeric_error = error; 286 } 287 288 if (show) { 289 errln(/*value.getString(temp) +*/ typeOf(value, temp) + " F> " + 290 escape(s) + " P> " + (n.getType() == Formattable::kDouble ? n.getDouble() : (double)n.getLong()) 291 /*n.getString(temp) */ + typeOf(n, temp) + " F> " + 292 escape(s2)); 293 } 294 } 295 296 double 297 NumberFormatRoundTripTest::proportionalError(const Formattable& a, const Formattable& b) 298 { 299 double aa,bb; 300 301 if(isDouble(a)) 302 aa = a.getDouble(); 303 else 304 aa = a.getLong(); 305 306 if(isDouble(b)) 307 bb = b.getDouble(); 308 else 309 bb = b.getLong(); 310 311 double error = aa - bb; 312 if(aa != 0 && bb != 0) 313 error /= aa; 314 315 return uprv_fabs(error); 316 } 317 318 UnicodeString& 319 NumberFormatRoundTripTest::typeOf(const Formattable& n, UnicodeString& result) 320 { 321 if(n.getType() == Formattable::kLong) { 322 result = UnicodeString(" Long"); 323 } 324 else if(n.getType() == Formattable::kDouble) { 325 result = UnicodeString(" Double"); 326 } 327 else if(n.getType() == Formattable::kString) { 328 result = UnicodeString(" UnicodeString"); 329 UnicodeString temp; 330 } 331 332 return result; 333 } 334 335 336 UnicodeString& 337 NumberFormatRoundTripTest::escape(UnicodeString& s) 338 { 339 UnicodeString copy(s); 340 s.remove(); 341 for(int i = 0; i < copy.length(); ++i) { 342 UChar c = copy[i]; 343 if(c < 0x00FF) 344 s += c; 345 else { 346 s += "+U"; 347 char temp[16]; 348 sprintf(temp, "%4X", c); // might not work 349 s += temp; 350 } 351 } 352 return s; 353 } 354 355 #endif /* #if !UCONFIG_NO_FORMATTING */ 356