1 /* @(#)fconv.c 1.45 10/11/06 Copyright 1985, 1995-2010 J. Schilling */ 2 /* 3 * Convert floating point numbers to strings for format.c 4 * Should rather use the MT-safe routines [efg]convert() 5 * 6 * Copyright (c) 1985, 1995-2010 J. Schilling 7 */ 8 /* 9 * The contents of this file are subject to the terms of the 10 * Common Development and Distribution License, Version 1.0 only 11 * (the "License"). You may not use this file except in compliance 12 * with the License. 13 * 14 * See the file CDDL.Schily.txt in this distribution for details. 15 * 16 * When distributing Covered Code, include this CDDL HEADER in each 17 * file and include the License file CDDL.Schily.txt from this distribution. 18 */ 19 20 #include <schily/mconfig.h> /* <- may define NO_FLOATINGPOINT */ 21 #ifndef NO_FLOATINGPOINT 22 23 #ifndef __DO_LONG_DOUBLE__ 24 #include <schily/stdlib.h> 25 #include <schily/standard.h> 26 #include <schily/string.h> 27 #include <schily/schily.h> 28 #include <schily/math.h> /* The default place for isinf()/isnan() */ 29 #include <schily/nlsdefs.h> 30 31 #if !defined(HAVE_STDLIB_H) || defined(HAVE_DTOA) 32 extern char *ecvt __PR((double, int, int *, int *)); 33 extern char *fcvt __PR((double, int, int *, int *)); 34 #endif 35 36 #if defined(HAVE_ISNAN) && defined(HAVE_ISINF) 37 /* 38 * *BSD alike libc 39 */ 40 #define FOUND_ISNAN 41 #define FOUND_ISINF 42 #define FOUND_ISXX 43 #endif 44 45 #if defined(HAVE_C99_ISNAN) && defined(HAVE_C99_ISINF) 46 #ifndef FOUND_ISXX 47 #define FOUND_ISXX 48 #endif 49 #define FOUND_C99_ISNAN 50 #define FOUND_C99_ISINF 51 #define FOUND_C99_ISXX 52 #endif 53 54 #if defined(HAVE_FP_H) && !defined(FOUND_ISXX) 55 /* 56 * WAS: 57 * #if defined(__osf__) || defined(_IBMR2) || defined(_AIX) 58 */ 59 /* 60 * Moved before HAVE_IEEEFP_H for True64 due to a hint 61 * from Bert De Knuydt <Bert.Deknuydt@esat.kuleuven.ac.be> 62 * 63 * True64 has both fp.h & ieeefp.h but the functions 64 * isnand() & finite() seem to be erreneously not implemented 65 * as a macro and the function lives in libm. 66 * Let's hope that we will not get problems with the new order. 67 */ 68 #include <fp.h> 69 #if !defined(isnan) && defined(IS_NAN) 70 #define isnan IS_NAN 71 #define FOUND_ISNAN 72 #endif 73 #if !defined(isinf) && defined(FINITE) 74 #define isinf !FINITE 75 #define FOUND_ISINF 76 #endif 77 #if defined(FOUND_ISNAN) && defined(FOUND_ISINF) 78 #define FOUND_ISXX 79 #endif 80 #endif 81 82 #if defined(HAVE_IEEEFP_H) && !defined(FOUND_ISXX) && \ 83 !defined(FOUND_C99_ISXX) 84 /* 85 * SVR4 86 */ 87 #include <ieeefp.h> 88 #ifdef HAVE_ISNAND 89 #ifndef isnan 90 #define isnan isnand 91 #define FOUND_ISNAN 92 #endif 93 #endif 94 #ifdef HAVE_FINITE 95 #ifndef isinf 96 #define isinf !finite 97 #define FOUND_ISINF 98 #endif 99 #endif 100 #if defined(FOUND_ISNAN) && defined(FOUND_ISINF) 101 #define FOUND_ISXX 102 #endif 103 #endif 104 105 /* 106 * WAS: 107 * #if defined(__hpux) || defined(VMS) || defined(_SCO_DS) || defined(__QNX__) 108 */ 109 #ifdef __nneded__ 110 #if defined(__hpux) || defined(__QNX__) || defined(__DJGPP__) 111 #ifndef FOUND_C99_ISXX 112 #undef isnan 113 #undef isinf 114 #endif 115 #endif 116 #endif /* __needed__ */ 117 118 /* 119 * As we no longer check for defined(isnan)/defined(isinf), the next block 120 * should also handle the problems with DJGPP, HP-UX, QNX and VMS. 121 */ 122 #if !defined(FOUND_ISNAN) && !defined(HAVE_C99_ISNAN) 123 #undef isnan 124 #define isnan(val) (0) 125 #define NO_ISNAN 126 #endif 127 #if !defined(FOUND_ISINF) && !defined(HAVE_C99_ISINF) 128 #undef isinf 129 #define isinf(val) (0) 130 #define NO_ISINF 131 #endif 132 133 #if defined(NO_ISNAN) || defined(NO_ISINF) 134 #include <schily/float.h> /* For values.h */ 135 #if (_IEEE - 0) > 0 /* We know that there is IEEE FP */ 136 /* 137 * Note that older HP-UX versions have different #defines for MAXINT in 138 * values.h and sys/param.h 139 */ 140 #include <schily/utypes.h> 141 #include <schily/btorder.h> 142 143 #ifdef WORDS_BIGENDIAN 144 #define fpw_high(x) ((UInt32_t *)&x)[0] 145 #define fpw_low(x) ((UInt32_t *)&x)[1] 146 #else 147 #define fpw_high(x) ((UInt32_t *)&x)[1] 148 #define fpw_low(x) ((UInt32_t *)&x)[0] 149 #endif 150 #define FP_EXP 0x7FF00000 151 #define fp_exp(x) (fpw_high(x) & FP_EXP) 152 #define fp_exc(x) (fp_exp(x) == FP_EXP) 153 154 #ifdef NO_ISNAN 155 #undef isnan 156 #define isnan(val) (fp_exc(val) && \ 157 (fpw_low(val) != 0 || (fpw_high(val) & 0xFFFFF) != 0)) 158 #endif 159 #ifdef NO_ISINF 160 #undef isinf 161 #define isinf(val) (fp_exc(val) && \ 162 fpw_low(val) == 0 && (fpw_high(val) & 0xFFFFF) == 0) 163 #endif 164 #endif /* We know that there is IEEE FP */ 165 #endif /* defined(NO_ISNAN) || defined(NO_ISINF) */ 166 167 168 #if !defined(HAVE_ECVT) || !defined(HAVE_FCVT) || !defined(HAVE_GCVT) 169 170 #ifdef NO_USER_XCVT 171 /* 172 * We cannot define our own ecvt()/fcvt()/gcvt() so we need to use 173 * local names instead. 174 */ 175 #ifndef HAVE_ECVT 176 #define ecvt js_ecvt 177 #endif 178 #ifndef HAVE_FCVT 179 #define fcvt js_fcvt 180 #endif 181 #ifndef HAVE_GCVT 182 #define gcvt js_gcvt 183 #endif 184 #endif 185 186 #include "cvt.c" 187 #endif 188 189 static char _js_nan[] = "(NaN)"; 190 static char _js_inf[] = "(Infinity)"; 191 192 static int _ferr __PR((char *, double)); 193 #endif /* __DO_LONG_DOUBLE__ */ 194 195 #ifdef __DO_LONG_DOUBLE__ 196 #undef MDOUBLE 197 #define MDOUBLE long double 198 #else 199 #undef MDOUBLE 200 #define MDOUBLE double 201 #endif 202 203 #ifdef abs 204 #undef abs 205 #endif 206 #define abs(i) ((i) < 0 ? -(i) : (i)) 207 208 EXPORT int 209 ftoes(s, val, fieldwidth, ndigits) 210 register char *s; 211 MDOUBLE val; 212 register int fieldwidth; 213 register int ndigits; 214 { 215 register char *b; 216 register char *rs; 217 register int len; 218 register int rdecpt; 219 int decpt; 220 int sign; 221 222 #ifndef __DO_LONG_DOUBLE__ 223 if ((len = _ferr(s, val)) > 0) 224 return (len); 225 #endif 226 rs = s; 227 #ifdef V7_FLOATSTYLE 228 b = ecvt(val, ndigits, &decpt, &sign); 229 rdecpt = decpt; 230 #else 231 b = ecvt(val, ndigits+1, &decpt, &sign); 232 rdecpt = decpt-1; 233 #endif 234 #ifdef __DO_LONG_DOUBLE__ 235 len = *b; 236 if (len < '0' || len > '9') { /* Inf/NaN */ 237 strcpy(s, b); 238 return (strlen(b)); 239 } 240 #endif 241 len = ndigits + 6; /* Punkt e +/- nnn */ 242 if (sign) 243 len++; 244 if (fieldwidth > len) 245 while (fieldwidth-- > len) 246 *rs++ = ' '; 247 if (sign) 248 *rs++ = '-'; 249 #ifndef V7_FLOATSTYLE 250 if (*b) 251 *rs++ = *b++; 252 #endif 253 #if defined(HAVE_LOCALECONV) && defined(USE_LOCALE) 254 *rs++ = *(localeconv()->decimal_point); 255 #else 256 *rs++ = '.'; 257 #endif 258 while (*b && ndigits-- > 0) 259 *rs++ = *b++; 260 *rs++ = 'e'; 261 *rs++ = rdecpt >= 0 ? '+' : '-'; 262 rdecpt = abs(rdecpt); 263 #ifdef __DO_LONG_DOUBLE__ 264 if (rdecpt >= 1000) { /* Max-Exp is > 4000 */ 265 *rs++ = rdecpt / 1000 + '0'; 266 rdecpt %= 1000; 267 } 268 #endif 269 #ifndef V7_FLOATSTYLE 270 if (rdecpt >= 100) 271 #endif 272 { 273 *rs++ = rdecpt / 100 + '0'; 274 rdecpt %= 100; 275 } 276 *rs++ = rdecpt / 10 + '0'; 277 *rs++ = rdecpt % 10 + '0'; 278 *rs = '\0'; 279 return (rs - s); 280 } 281 282 /* 283 * fcvt() from Cygwin32 is buggy. 284 */ 285 #if !defined(HAVE_FCVT) && defined(HAVE_ECVT) 286 #define USE_ECVT 287 #endif 288 289 EXPORT int 290 ftofs(s, val, fieldwidth, ndigits) 291 register char *s; 292 MDOUBLE val; 293 register int fieldwidth; 294 register int ndigits; 295 { 296 register char *b; 297 register char *rs; 298 register int len; 299 register int rdecpt; 300 int decpt; 301 int sign; 302 303 #ifndef __DO_LONG_DOUBLE__ 304 if ((len = _ferr(s, val)) > 0) 305 return (len); 306 #endif 307 rs = s; 308 #ifdef USE_ECVT 309 /* 310 * Needed on systems with broken fcvt() implementation 311 * (e.g. Cygwin32) 312 */ 313 b = ecvt(val, ndigits, &decpt, &sign); 314 /* 315 * The next call is needed to force higher precision. 316 */ 317 if (decpt > 0) 318 b = ecvt(val, ndigits+decpt, &decpt, &sign); 319 #else 320 b = fcvt(val, ndigits, &decpt, &sign); 321 #endif 322 #ifdef __DO_LONG_DOUBLE__ 323 len = *b; 324 if (len < '0' || len > '9') { /* Inf/NaN */ 325 strcpy(s, b); 326 return (strlen(b)); 327 } 328 #endif 329 rdecpt = decpt; 330 len = rdecpt + ndigits + 1; 331 if (rdecpt < 0) 332 len -= rdecpt; 333 if (sign) 334 len++; 335 if (fieldwidth > len) 336 while (fieldwidth-- > len) 337 *rs++ = ' '; 338 if (sign) 339 *rs++ = '-'; 340 if (rdecpt > 0) { 341 len = rdecpt; 342 while (*b && len-- > 0) 343 *rs++ = *b++; 344 #ifdef USE_ECVT 345 while (len-- > 0) 346 *rs++ = '0'; 347 #endif 348 } 349 #ifndef V7_FLOATSTYLE 350 else { 351 *rs++ = '0'; 352 } 353 #endif 354 #if defined(HAVE_LOCALECONV) && defined(USE_LOCALE) 355 *rs++ = *(localeconv()->decimal_point); 356 #else 357 *rs++ = '.'; 358 #endif 359 if (rdecpt < 0) { 360 len = rdecpt; 361 while (len++ < 0 && ndigits-- > 0) 362 *rs++ = '0'; 363 } 364 while (*b && ndigits-- > 0) 365 *rs++ = *b++; 366 #ifdef USE_ECVT 367 while (ndigits-- > 0) 368 *rs++ = '0'; 369 #endif 370 *rs = '\0'; 371 return (rs - s); 372 } 373 374 #ifndef __DO_LONG_DOUBLE__ 375 376 #ifdef HAVE_LONGDOUBLE 377 #ifdef HAVE__LDECVT 378 #define qecvt(ld, n, dp, sp) _ldecvt(*(long_double *)&ld, n, dp, sp) 379 #endif 380 #ifdef HAVE__LDFCVT 381 #define qfcvt(ld, n, dp, sp) _ldfcvt(*(long_double *)&ld, n, dp, sp) 382 #endif 383 384 #if (defined(HAVE_QECVT) || defined(HAVE__LDECVT)) && \ 385 (defined(HAVE_QFCVT) || defined(HAVE__LDFCVT)) 386 #define __DO_LONG_DOUBLE__ 387 #define ftoes qftoes 388 #define ftofs qftofs 389 #define ecvt qecvt 390 #define fcvt qfcvt 391 #include "fconv.c" 392 #undef __DO_LONG_DOUBLE__ 393 #endif 394 #endif /* HAVE_LONGDOUBLE */ 395 396 LOCAL int 397 _ferr(s, val) 398 char *s; 399 double val; 400 { 401 if (isnan(val)) { 402 strcpy(s, _js_nan); 403 return (sizeof (_js_nan) - 1); 404 } 405 406 /* 407 * Check first for NaN because finite() will return 1 on Nan too. 408 */ 409 if (isinf(val)) { 410 strcpy(s, _js_inf); 411 return (sizeof (_js_inf) - 1); 412 } 413 return (0); 414 } 415 #endif /* __DO_LONG_DOUBLE__ */ 416 #endif /* NO_FLOATINGPOINT */ 417