/* @(#)fconv.c 1.45 10/11/06 Copyright 1985, 1995-2010 J. Schilling */ /* * Convert floating point numbers to strings for format.c * Should rather use the MT-safe routines [efg]convert() * * Copyright (c) 1985, 1995-2010 J. Schilling */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #include /* <- may define NO_FLOATINGPOINT */ #ifndef NO_FLOATINGPOINT #ifndef __DO_LONG_DOUBLE__ #include #include #include #include #include /* The default place for isinf()/isnan() */ #include #if !defined(HAVE_STDLIB_H) || defined(HAVE_DTOA) extern char *ecvt __PR((double, int, int *, int *)); extern char *fcvt __PR((double, int, int *, int *)); #endif #if defined(HAVE_ISNAN) && defined(HAVE_ISINF) /* * *BSD alike libc */ #define FOUND_ISNAN #define FOUND_ISINF #define FOUND_ISXX #endif #if defined(HAVE_C99_ISNAN) && defined(HAVE_C99_ISINF) #ifndef FOUND_ISXX #define FOUND_ISXX #endif #define FOUND_C99_ISNAN #define FOUND_C99_ISINF #define FOUND_C99_ISXX #endif #if defined(HAVE_FP_H) && !defined(FOUND_ISXX) /* * WAS: * #if defined(__osf__) || defined(_IBMR2) || defined(_AIX) */ /* * Moved before HAVE_IEEEFP_H for True64 due to a hint * from Bert De Knuydt * * True64 has both fp.h & ieeefp.h but the functions * isnand() & finite() seem to be erreneously not implemented * as a macro and the function lives in libm. * Let's hope that we will not get problems with the new order. */ #include #if !defined(isnan) && defined(IS_NAN) #define isnan IS_NAN #define FOUND_ISNAN #endif #if !defined(isinf) && defined(FINITE) #define isinf !FINITE #define FOUND_ISINF #endif #if defined(FOUND_ISNAN) && defined(FOUND_ISINF) #define FOUND_ISXX #endif #endif #if defined(HAVE_IEEEFP_H) && !defined(FOUND_ISXX) && \ !defined(FOUND_C99_ISXX) /* * SVR4 */ #include #ifdef HAVE_ISNAND #ifndef isnan #define isnan isnand #define FOUND_ISNAN #endif #endif #ifdef HAVE_FINITE #ifndef isinf #define isinf !finite #define FOUND_ISINF #endif #endif #if defined(FOUND_ISNAN) && defined(FOUND_ISINF) #define FOUND_ISXX #endif #endif /* * WAS: * #if defined(__hpux) || defined(VMS) || defined(_SCO_DS) || defined(__QNX__) */ #ifdef __nneded__ #if defined(__hpux) || defined(__QNX__) || defined(__DJGPP__) #ifndef FOUND_C99_ISXX #undef isnan #undef isinf #endif #endif #endif /* __needed__ */ /* * As we no longer check for defined(isnan)/defined(isinf), the next block * should also handle the problems with DJGPP, HP-UX, QNX and VMS. */ #if !defined(FOUND_ISNAN) && !defined(HAVE_C99_ISNAN) #undef isnan #define isnan(val) (0) #define NO_ISNAN #endif #if !defined(FOUND_ISINF) && !defined(HAVE_C99_ISINF) #undef isinf #define isinf(val) (0) #define NO_ISINF #endif #if defined(NO_ISNAN) || defined(NO_ISINF) #include /* For values.h */ #if (_IEEE - 0) > 0 /* We know that there is IEEE FP */ /* * Note that older HP-UX versions have different #defines for MAXINT in * values.h and sys/param.h */ #include #include #ifdef WORDS_BIGENDIAN #define fpw_high(x) ((UInt32_t *)&x)[0] #define fpw_low(x) ((UInt32_t *)&x)[1] #else #define fpw_high(x) ((UInt32_t *)&x)[1] #define fpw_low(x) ((UInt32_t *)&x)[0] #endif #define FP_EXP 0x7FF00000 #define fp_exp(x) (fpw_high(x) & FP_EXP) #define fp_exc(x) (fp_exp(x) == FP_EXP) #ifdef NO_ISNAN #undef isnan #define isnan(val) (fp_exc(val) && \ (fpw_low(val) != 0 || (fpw_high(val) & 0xFFFFF) != 0)) #endif #ifdef NO_ISINF #undef isinf #define isinf(val) (fp_exc(val) && \ fpw_low(val) == 0 && (fpw_high(val) & 0xFFFFF) == 0) #endif #endif /* We know that there is IEEE FP */ #endif /* defined(NO_ISNAN) || defined(NO_ISINF) */ #if !defined(HAVE_ECVT) || !defined(HAVE_FCVT) || !defined(HAVE_GCVT) #ifdef NO_USER_XCVT /* * We cannot define our own ecvt()/fcvt()/gcvt() so we need to use * local names instead. */ #ifndef HAVE_ECVT #define ecvt js_ecvt #endif #ifndef HAVE_FCVT #define fcvt js_fcvt #endif #ifndef HAVE_GCVT #define gcvt js_gcvt #endif #endif #include "cvt.c" #endif static char _js_nan[] = "(NaN)"; static char _js_inf[] = "(Infinity)"; static int _ferr __PR((char *, double)); #endif /* __DO_LONG_DOUBLE__ */ #ifdef __DO_LONG_DOUBLE__ #undef MDOUBLE #define MDOUBLE long double #else #undef MDOUBLE #define MDOUBLE double #endif #ifdef abs #undef abs #endif #define abs(i) ((i) < 0 ? -(i) : (i)) EXPORT int ftoes(s, val, fieldwidth, ndigits) register char *s; MDOUBLE val; register int fieldwidth; register int ndigits; { register char *b; register char *rs; register int len; register int rdecpt; int decpt; int sign; #ifndef __DO_LONG_DOUBLE__ if ((len = _ferr(s, val)) > 0) return (len); #endif rs = s; #ifdef V7_FLOATSTYLE b = ecvt(val, ndigits, &decpt, &sign); rdecpt = decpt; #else b = ecvt(val, ndigits+1, &decpt, &sign); rdecpt = decpt-1; #endif #ifdef __DO_LONG_DOUBLE__ len = *b; if (len < '0' || len > '9') { /* Inf/NaN */ strcpy(s, b); return (strlen(b)); } #endif len = ndigits + 6; /* Punkt e +/- nnn */ if (sign) len++; if (fieldwidth > len) while (fieldwidth-- > len) *rs++ = ' '; if (sign) *rs++ = '-'; #ifndef V7_FLOATSTYLE if (*b) *rs++ = *b++; #endif #if defined(HAVE_LOCALECONV) && defined(USE_LOCALE) *rs++ = *(localeconv()->decimal_point); #else *rs++ = '.'; #endif while (*b && ndigits-- > 0) *rs++ = *b++; *rs++ = 'e'; *rs++ = rdecpt >= 0 ? '+' : '-'; rdecpt = abs(rdecpt); #ifdef __DO_LONG_DOUBLE__ if (rdecpt >= 1000) { /* Max-Exp is > 4000 */ *rs++ = rdecpt / 1000 + '0'; rdecpt %= 1000; } #endif #ifndef V7_FLOATSTYLE if (rdecpt >= 100) #endif { *rs++ = rdecpt / 100 + '0'; rdecpt %= 100; } *rs++ = rdecpt / 10 + '0'; *rs++ = rdecpt % 10 + '0'; *rs = '\0'; return (rs - s); } /* * fcvt() from Cygwin32 is buggy. */ #if !defined(HAVE_FCVT) && defined(HAVE_ECVT) #define USE_ECVT #endif EXPORT int ftofs(s, val, fieldwidth, ndigits) register char *s; MDOUBLE val; register int fieldwidth; register int ndigits; { register char *b; register char *rs; register int len; register int rdecpt; int decpt; int sign; #ifndef __DO_LONG_DOUBLE__ if ((len = _ferr(s, val)) > 0) return (len); #endif rs = s; #ifdef USE_ECVT /* * Needed on systems with broken fcvt() implementation * (e.g. Cygwin32) */ b = ecvt(val, ndigits, &decpt, &sign); /* * The next call is needed to force higher precision. */ if (decpt > 0) b = ecvt(val, ndigits+decpt, &decpt, &sign); #else b = fcvt(val, ndigits, &decpt, &sign); #endif #ifdef __DO_LONG_DOUBLE__ len = *b; if (len < '0' || len > '9') { /* Inf/NaN */ strcpy(s, b); return (strlen(b)); } #endif rdecpt = decpt; len = rdecpt + ndigits + 1; if (rdecpt < 0) len -= rdecpt; if (sign) len++; if (fieldwidth > len) while (fieldwidth-- > len) *rs++ = ' '; if (sign) *rs++ = '-'; if (rdecpt > 0) { len = rdecpt; while (*b && len-- > 0) *rs++ = *b++; #ifdef USE_ECVT while (len-- > 0) *rs++ = '0'; #endif } #ifndef V7_FLOATSTYLE else { *rs++ = '0'; } #endif #if defined(HAVE_LOCALECONV) && defined(USE_LOCALE) *rs++ = *(localeconv()->decimal_point); #else *rs++ = '.'; #endif if (rdecpt < 0) { len = rdecpt; while (len++ < 0 && ndigits-- > 0) *rs++ = '0'; } while (*b && ndigits-- > 0) *rs++ = *b++; #ifdef USE_ECVT while (ndigits-- > 0) *rs++ = '0'; #endif *rs = '\0'; return (rs - s); } #ifndef __DO_LONG_DOUBLE__ #ifdef HAVE_LONGDOUBLE #ifdef HAVE__LDECVT #define qecvt(ld, n, dp, sp) _ldecvt(*(long_double *)&ld, n, dp, sp) #endif #ifdef HAVE__LDFCVT #define qfcvt(ld, n, dp, sp) _ldfcvt(*(long_double *)&ld, n, dp, sp) #endif #if (defined(HAVE_QECVT) || defined(HAVE__LDECVT)) && \ (defined(HAVE_QFCVT) || defined(HAVE__LDFCVT)) #define __DO_LONG_DOUBLE__ #define ftoes qftoes #define ftofs qftofs #define ecvt qecvt #define fcvt qfcvt #include "fconv.c" #undef __DO_LONG_DOUBLE__ #endif #endif /* HAVE_LONGDOUBLE */ LOCAL int _ferr(s, val) char *s; double val; { if (isnan(val)) { strcpy(s, _js_nan); return (sizeof (_js_nan) - 1); } /* * Check first for NaN because finite() will return 1 on Nan too. */ if (isinf(val)) { strcpy(s, _js_inf); return (sizeof (_js_inf) - 1); } return (0); } #endif /* __DO_LONG_DOUBLE__ */ #endif /* NO_FLOATINGPOINT */