From 6bdac416e9e1aecfb94cd3ae383889cc7b7e62e3 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Wed, 6 Dec 2000 23:50:11 +0000 Subject: 2000-12-06 Jeff Johnston * libc/stdlib/Makefile.am: Added ldtoa.c to list of sources. * libc/stdlib/Makefile.in: Regenerated. * libc/stdio/floatio.h: Added suitable MAXEXP for long double. * libc/stdio/vfieeefp.h: Added long double bit structures. * libc/stdio/vfprintf.c[WANT_IO_LONG_DBL]: Added long double support. [WANT_IO_LONG_DBL](isinfl, isnanl): New static long double routines. (exponent): Changed expbuf to reasonable maximum instead of MAXEXP. * libc/stdio/vfscanf.c[WANT_IO_LONG_DBL]: Added long double support. * libc/stdlib/ldtoa.c: New file containing _ldtoa_r and _strtold routines used for conversions between character and long double. --- newlib/libc/stdio/floatio.h | 5 ++ newlib/libc/stdio/vfieeefp.h | 79 ++++++++++++++++++++++++++ newlib/libc/stdio/vfprintf.c | 130 ++++++++++++++++++++++++++++++++++++++----- newlib/libc/stdio/vfscanf.c | 28 ++++++++-- 4 files changed, 223 insertions(+), 19 deletions(-) (limited to 'newlib/libc/stdio') diff --git a/newlib/libc/stdio/floatio.h b/newlib/libc/stdio/floatio.h index d9577b2b2..496721b88 100644 --- a/newlib/libc/stdio/floatio.h +++ b/newlib/libc/stdio/floatio.h @@ -21,7 +21,12 @@ * Floating point scanf/printf (input/output) definitions. */ +#ifdef _NO_LONGDBL /* 11-bit exponent (VAX G floating point) is 308 decimal digits */ #define MAXEXP 308 +#else /* !_NO_LONGDBL */ +/* 15-bit exponent (Intel extended floating point) is 4932 decimal digits */ +#define MAXEXP 4932 +#endif /* !_NO_LONGDBL */ /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */ #define MAXFRACT 39 diff --git a/newlib/libc/stdio/vfieeefp.h b/newlib/libc/stdio/vfieeefp.h index 6843d5f47..de0267223 100644 --- a/newlib/libc/stdio/vfieeefp.h +++ b/newlib/libc/stdio/vfieeefp.h @@ -57,6 +57,85 @@ Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. #endif +#ifdef WANT_IO_LONG_DBL +/* If we are going to examine or modify specific bits in a long double using + the lword0 or lwordx macros, then we must wrap the long double inside + a union. This is necessary to avoid undefined behavior according to + the ANSI C spec. */ + +#ifdef IEEE_8087 +#if LDBL_MANT_DIG == 24 +struct ldieee +{ + unsigned manh:23; + unsigned exp:8; + unsigned sign:1; +} +#elif LDBL_MANT_DIG == 53 +struct ldieee +{ + unsigned manl:20; + unsigned manh:32; + unsigned exp:11; + unsigned sign:1; +} +#elif LDBL_MANT_DIG == 64 +struct ldieee +{ + unsigned manl:32; + unsigned manh:32; + unsigned exp:15; + unsigned sign:1; +}; +#elif LDBL_MANT_DIG > 64 +struct ldieee +{ + unsigned manl3:16; + unsigned manl2:32; + unsigned manl:32; + unsigned manh:32; + unsigned exp:15; + unsigned sign:1; +}; +#endif /* LDBL_MANT_DIG */ +#else /* !IEEE_8087 */ +#if LDBL_MANT_DIG == 24 +struct ldieee +{ + unsigned sign:1; + unsigned exp:8; + unsigned manh:23; +} +#elif LDBL_MANT_DIG == 53 +struct ldieee +{ + unsigned sign:1; + unsigned exp:11; + unsigned manh:32; + unsigned manl:20; +} +#elif LDBL_MANT_DIG == 64 +struct ldieee +{ + unsigned sign:1; + unsigned exp:15; + unsigned manh:32; + unsigned manl:32; +} +#elif LDBL_MANT_DIG > 64 +struct ldieee +{ + unsigned sign:1; + unsigned exp:15; + unsigned manh:32; + unsigned manl:32; + unsigned manl2:32; + unsigned manl3;16; +}; +#endif /* LDBL_MANT_DIG */ +#endif /* !IEEE_8087 */ +#endif /* WANT_IO_LONG_DBL */ + /* If we are going to examine or modify specific bits in a double using the word0 and/or word1 macros, then we must wrap the double inside a union. This is necessary to avoid undefined behavior according to diff --git a/newlib/libc/stdio/vfprintf.c b/newlib/libc/stdio/vfprintf.c index 117f7eb80..ea458467d 100644 --- a/newlib/libc/stdio/vfprintf.c +++ b/newlib/libc/stdio/vfprintf.c @@ -174,6 +174,14 @@ static char *rcsid = "$Id$"; #include "fvwrite.h" #include "vfieeefp.h" +/* Currently a test is made to see if long double processing is warranted. + This could be changed in the future should the _ldtoa_r code be + preferred over _dtoa_r. */ +#define _NO_LONGDBL +#if defined WANT_IO_LONG_DBL && (LDBL_MANT_DIG > DBL_MANT_DIG) +#undef _NO_LONGDBL +#endif + /* * Flush out all the vectors defined by the given uio, * then reset it so that it can be reused. @@ -240,7 +248,14 @@ __sbprintf(fp, fmt, ap) #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ #define DEFPREC 6 +#ifdef _NO_LONGDBL static char *cvt _PARAMS((struct _reent *, double, int, int, char *, int *, int, int *)); +#else +static char *cvt _PARAMS((struct _reent *, _LONG_DOUBLE, int, int, char *, int *, int, int *)); +static int isinfl _PARAMS((_LONG_DOUBLE *)); +static int isnanl _PARAMS((_LONG_DOUBLE *)); +#endif + static int exponent _PARAMS((char *, int, int)); #else /* no FLOATING_POINT */ @@ -263,7 +278,7 @@ static int exponent _PARAMS((char *, int, int)); #define ALT 0x001 /* alternate form */ #define HEXPREFIX 0x002 /* add 0x or 0X prefix */ #define LADJUST 0x004 /* left adjustment */ -#define LONGDBL 0x008 /* long double; unimplemented */ +#define LONGDBL 0x008 /* long double */ #define LONGINT 0x010 /* long integer */ #define QUADINT 0x020 /* quad integer */ #define SHORTINT 0x040 /* short integer */ @@ -301,9 +316,13 @@ _DEFUN (_VFPRINTF_R, (data, fp, fmt0, ap), #ifdef FLOATING_POINT char *decimal_point = localeconv()->decimal_point; char softsign; /* temporary negative sign for floats */ - double _double; /* double precision arguments %[eEfgG] */ +#ifdef _NO_LONGDBL + double _fpvalue; /* floating point arguments %[eEfgG] */ +#else + _LONG_DOUBLE _fpvalue; /* floating point arguments %[eEfgG] */ +#endif int expt; /* integer value of exponent */ - int expsize; /* character count for expstr */ + int expsize = 0; /* character count for expstr */ int ndig; /* actual number of digits returned by cvt */ char expstr[7]; /* buffer for exponent string */ #endif @@ -322,7 +341,7 @@ _DEFUN (_VFPRINTF_R, (data, fp, fmt0, ap), int dprec; /* a copy of prec if [diouxX], 0 otherwise */ int realsz; /* field size expanded by dprec */ int size; /* size of converted field or string */ - char *xdigs; /* digits for [xX] conversion */ + char *xdigs = NULL; /* digits for [xX] conversion */ #define NIOV 8 struct __suio uio; /* output information: summary */ struct __siov iov[NIOV];/* ... and individual io vectors */ @@ -553,29 +572,55 @@ reswitch: switch (ch) { prec = 1; } +#ifdef _NO_LONGDBL + if (flags & LONGDBL) { + _fpvalue = (double) va_arg(ap, _LONG_DOUBLE); + } else { + _fpvalue = va_arg(ap, double); + } + + /* do this before tricky precision changes */ + if (isinf(_fpvalue)) { + if (_fpvalue < 0) + sign = '-'; + cp = "Inf"; + size = 3; + break; + } + if (isnan(_fpvalue)) { + cp = "NaN"; + size = 3; + break; + } + +#else /* !_NO_LONGDBL */ + if (flags & LONGDBL) { - _double = (double) va_arg(ap, long double); + _fpvalue = va_arg(ap, _LONG_DOUBLE); } else { - _double = va_arg(ap, double); + _fpvalue = (_LONG_DOUBLE)va_arg(ap, double); } /* do this before tricky precision changes */ - if (isinf(_double)) { - if (_double < 0) + if (isinfl(&_fpvalue)) { + if (_fpvalue < 0) sign = '-'; cp = "Inf"; size = 3; break; } - if (isnan(_double)) { + if (isnanl(&_fpvalue)) { cp = "NaN"; size = 3; break; } +#endif /* !_NO_LONGDBL */ flags |= FPT; - cp = cvt(data, _double, prec, flags, &softsign, + + cp = cvt(data, _fpvalue, prec, flags, &softsign, &expt, ch, &ndig); + if (ch == 'g' || ch == 'G') { if (expt <= -4 || expt > prec) ch = (ch == 'g') ? 'e' : 'E'; @@ -796,7 +841,7 @@ number: if ((dprec = prec) >= 0) PRINT(cp, size); } else { /* glue together f_p fragments */ if (ch >= 'f') { /* 'f' or 'g' */ - if (_double == 0) { + if (_fpvalue == 0) { /* kludge for __dtoa irregularity */ PRINT("0", 1); if (expt < ndig || (flags & ALT) != 0) { @@ -826,7 +871,7 @@ number: if ((dprec = prec) >= 0) ox[0] = *cp++; ox[1] = '.'; PRINT(ox, 2); - if (_double) { + if (_fpvalue) { PRINT(cp, ndig-1); } else /* 0.[0..] */ /* __dtoa irregularity */ @@ -857,19 +902,60 @@ error: #ifdef FLOATING_POINT +#ifdef _NO_LONGDBL extern char *_dtoa_r _PARAMS((struct _reent *, double, int, int, int *, int *, char **)); +#else +extern char *_ldtoa_r _PARAMS((struct _reent *, _LONG_DOUBLE, int, + int, int *, int *, char **)); +#undef word0 +#define word0(x) ldword0(x) + +static int +isinfl (value) + _LONG_DOUBLE *value; +{ + struct ldieee *ldptr; + + ldptr = (struct ldieee *)value; + + if (ldptr->exp == 0x7fff && !(ldptr->manh & 0x7fffffff) && !ldptr->manl) + return 1; + return 0; +} + +static int +isnanl (value) + _LONG_DOUBLE *value; +{ + struct ldieee *ldptr; + + ldptr = (struct ldieee *)value; + + if (ldptr->exp == 0x7fff && ((ldptr->manh & 0x7fffffff) || ldptr->manl)) + return 1; + return 0; +} +#endif static char * cvt(data, value, ndigits, flags, sign, decpt, ch, length) struct _reent *data; +#ifdef _NO_LONGDBL double value; +#else + _LONG_DOUBLE value; +#endif int ndigits, flags, *decpt, ch, *length; char *sign; { int mode, dsgn; char *digits, *bp, *rve; +#ifdef _NO_LONGDBL union double_union tmp; +#else + struct ldieee *ldptr; +#endif if (ch == 'f') { mode = 3; /* ndigits after the decimal point */ @@ -884,13 +970,27 @@ cvt(data, value, ndigits, flags, sign, decpt, ch, length) mode = 2; /* ndigits significant digits */ } +#ifdef _NO_LONGDBL tmp.d = value; + if (word0(tmp) & Sign_bit) { /* this will check for < 0 and -0.0 */ value = -value; *sign = '-'; } else *sign = '\000'; + digits = _dtoa_r(data, value, mode, ndigits, decpt, &dsgn, &rve); +#else /* !_NO_LONGDBL */ + ldptr = (struct ldieee *)&value; + if (ldptr->sign) { /* this will check for < 0 and -0.0 */ + value = -value; + *sign = '-'; + } else + *sign = '\000'; + + digits = _ldtoa_r(data, value, mode, ndigits, decpt, &dsgn, &rve); +#endif /* !_NO_LONGDBL */ + if ((ch != 'g' && ch != 'G') || flags & ALT) { /* Print trailing zeros */ bp = digits + ndigits; if (ch == 'f') { @@ -913,7 +1013,7 @@ exponent(p0, exp, fmtch) int exp, fmtch; { register char *p, *t; - char expbuf[MAXEXP]; + char expbuf[40]; p = p0; *p++ = fmtch; @@ -923,13 +1023,13 @@ exponent(p0, exp, fmtch) } else *p++ = '+'; - t = expbuf + MAXEXP; + t = expbuf + 40; if (exp > 9) { do { *--t = to_char(exp % 10); } while ((exp /= 10) > 9); *--t = to_char(exp); - for (; t < expbuf + MAXEXP; *p++ = *t++); + for (; t < expbuf + 40; *p++ = *t++); } else { *p++ = '0'; diff --git a/newlib/libc/stdio/vfscanf.c b/newlib/libc/stdio/vfscanf.c index 7e88f1202..cc0f26d3c 100644 --- a/newlib/libc/stdio/vfscanf.c +++ b/newlib/libc/stdio/vfscanf.c @@ -34,6 +34,17 @@ #endif #ifdef FLOATING_POINT +#include + +/* Currently a test is made to see if long double processing is warranted. + This could be changed in the future should the _ldtoa_r code be + preferred over _dtoa_r. */ +#define _NO_LONGDBL +#if defined WANT_IO_LONG_DBL && (LDBL_MANT_DIG > DBL_MANT_DIG) +#undef _NO_LONGDBL +extern _LONG_DOUBLE _strtold _PARAMS((char *s, char **sptr)); +#endif + #include "floatio.h" #define BUF (MAXEXP+MAXFRACT+3) /* 3 = sign + decimal point + NUL */ /* An upper bound for how long a long prints in decimal. 4 / 13 approximates @@ -48,7 +59,7 @@ */ #define LONG 0x01 /* l: long or double */ -#define LONGDBL 0x02 /* L: long double; unimplemented */ +#define LONGDBL 0x02 /* L: long double */ #define SHORT 0x04 /* h: short */ #define SUPPRESS 0x08 /* suppress assignment */ #define POINTER 0x10 /* weird %p pointer (`fake hex') */ @@ -372,7 +383,7 @@ __svfscanf (fp, fmt0, ap) for (;;) { - if ((n = fp->_r) < width) + if ((n = fp->_r) < (int)width) { sum += n; width -= n; @@ -683,8 +694,9 @@ __svfscanf (fp, fmt0, ap) However, ANSI / ISO C makes no such stipulation; we have to get exact results even when there is an unreasonable amount of leading zeroes. */ - long leading_zeroes, zeroes, exp_adjust; - char *exp_start; + long leading_zeroes = 0; + long zeroes, exp_adjust; + char *exp_start = NULL; #ifdef hardway if (width == 0 || width > sizeof (buf) - 1) width = sizeof (buf) - 1; @@ -808,7 +820,11 @@ __svfscanf (fp, fmt0, ap) } if ((flags & SUPPRESS) == 0) { +#ifdef _NO_LONGDBL double res; +#else /* !_NO_LONG_DBL */ + long double res; +#endif /* !_NO_LONG_DBL */ long new_exp; *p = 0; @@ -829,7 +845,11 @@ __svfscanf (fp, fmt0, ap) exp_start = buf + sizeof (buf) - MAX_LONG_LEN - 1; sprintf (exp_start, "e%ld", new_exp); } +#ifdef _NO_LONG_DBL res = atof (buf); +#else /* !_NO_LONG_DBL */ + res = _strtold (buf, NULL); +#endif /* !_NO_LONG_DBL */ if (flags & LONG) { dp = va_arg (ap, double *); -- cgit v1.2.3