File:  [MW Coherent from dump] / coherent / b / lib / libc / stdio / _dtefg.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:35 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * libc/stdio/_dtefg.c
 * ANSI-compliant C standard i/o library internals.
 * _dtefg(), _dtoa()
 * ANSI 4.9.6.1.
 * Floating point output conversion routines for 'printf'.
 * Conditionalized #if _IEEE to do 8087 conversion.
 */

#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include "stdio.int.h"

#if	0
#include <locale.h>
#else
#define	_decimal_point	'.'
#endif

extern	char	*_dtof();

#if	_IEEE
#include <stddef.h>
#include <string.h>

/*
 * This table is indexed by the return value of the "_fxam" routine.
 * The number is converted if the entry is NULL,
 * otherwise the entry gives the string to print.
 */
static char *fxamsg[] = {
	"{+ Unnormal}",
	"{+ NAN}",
	"{- Unnormal}",
	"{- NAN}",
	NULL,			/* + Normal	*/
	"{+ Infinity}",
	NULL,			/* - Normal	*/
	"{- Infinity}",
	NULL,			/* +0		*/
	NULL,			/* Empty	*/
	NULL,			/* -0		*/
	NULL,			/* Empty	*/
	"{+ Denormal}",
	NULL,			/* Empty	*/
	"{- Denormal}",
	NULL			/* Empty	*/
};
#endif

/*
 * Convert a floating point number 'd' from binary
 * into 'e', 'E', 'f', 'g', or 'G' format ASCII in the buffer 'cp'.
 * 'fmt' is the conversion type.
 * 'prec' is the precision.
 * 'aflag' is the '#' flag from the conversion specication.
 * 'signp' points to the returned sign (-1 negative, 1 nonnegative).
 * Return a pointer past the last character.
 *
 * Called from printf() for fp conversions and from gcvt().
 */
char *
_dtefg(cp, dp, fmt, prec, aflag, signp)
register char *cp; double *dp; int fmt, prec, aflag; int *signp;
{
	int	eflag, decexp;
	char	tbuf[DBL_DIG+1];
	double	d;

	d = *dp;
#if	_IEEE
	{
		register char *cp2;

		/* Print given string if 8087 format is special. */
		if ((cp2=fxamsg[_fxam(d)]) != NULL) {
			strcpy(cp, cp2);
			return cp + strlen(cp);
		}
	}
#endif

	if (prec == 0 && (fmt == 'g' || fmt == 'G'))
		prec = 1;
	else if (prec == -1)
		prec = 6;			/* Default precision */

	if (d < 0.0) {
		d = -d;				/* Force d nonnegative */
		*signp = -1;
	} else
		*signp = 1;
	_dtoa(fmt, &d, prec, &decexp, tbuf);
	eflag = (fmt=='e' || fmt=='E'
	      || ((fmt=='g' || fmt=='G') && (decexp < -4 || decexp >= prec)));
	cp = _dtof(cp, tbuf, prec, eflag ? 0 : decexp, fmt, aflag);
	if (eflag) {
		*cp++ = (fmt == 'E' || fmt == 'G') ? 'E' : 'e';
		cp += sprintf(cp, "%+03d", decexp);
	}
	return cp;
}

/*
 * Copy ASCII number from 'cp' to 'buf' in %f format
 * with precision 'prec' and decimal exponent 'decexp'.
 * The 'fmt' determines whether trailing zeros are suppressed.
 * Return a pointer past the last character.
 */
static
char *
_dtof(buf, cp, prec, decexp, fmt, aflag)
register char *buf, *cp; register int prec, decexp, fmt, aflag;
{
	if (decexp < 0)
		*buf++ = '0';			/* Units digit '0' */
	else
		do
			*buf++ = *cp ? *cp++ : '0';	/* integral part */
		while (decexp--);
	if (!aflag && (prec == 0 || ((fmt=='g'|| fmt=='G') && *cp == '\0')))
		return buf;
	*buf++ = _decimal_point;		/* '.' */
	while (prec-- > 0) {
		if ((fmt=='g' || fmt=='G') && *cp == '\0' && !aflag)
			break;			/* suppress trailing zeros */
		if (++decexp < 0)
			*buf++ = '0';		/* put leading zero */
		else
			*buf++ = *cp ? *cp++ : '0';
	}
	return buf;
}

/*
 * Convert the mantissa of nonnegative double 'd' to a string of ASCII digits
 * in the supplied buffer 'buf'.
 * Store the decimal exponent indirectly through 'decexpp'.
 * The first digit of the mantissa is implicitly followed by '.'
 * The result has no leading zeros (unless d=0.0) and no trailing zeros.
 * The precision 'prec' and format 'fmt' determine the digit count.
 * The maximum length of the result is DBL_DIG+1 (for the NUL).
 * For example, if *dp==123.456789 and prec==3:
 *	fmt=='e'	"1234"		decexp==2	1.234e+02
 *	fmt=='f'	"123456"	decexp==2	123.456
 *	fmt=='g'	"123"		decexp==2	123
 * This is called directly by ecvt() and fcvt(), as well as from _dtefg().
 */
void
_dtoa(fmt, dp, prec, decexpp, buf) int fmt; double *dp; int prec; int *decexpp; char *buf;
{
	register char *	cp;
	register int	digit;
	register int	decexp;
	int		ndigits;
	int		binexp;
	double		d;
	double		dexp;

	/* Handle 0.0 as a special case. */
	cp = buf;
	if ((d = *dp) == 0.0) {
ret0:
		*decexpp = 0;
		*cp++ = '0';
		*cp ='\0';
		return;
	}

	/* Reduce d to range [1., 10) and set decexp accordingly. */
	/* Approximate the decimal exponent from the binary exponent. */
	/* Obscure but it makes floating output much more efficient. */
	frexp(d, &binexp);			/* Find binary exponent */
	if (modf((--binexp)/LOG10B2, &dexp) < 0.0)
		dexp -= 1.0;			/* Scale, take integer part */
	decexp = dexp;				/* Convert to integer */
	d *= _pow10(-decexp);			/* Reduce d by power of 10 */
	if (d >= 10.) {				/* May be off by 1 place */
		++decexp;
		d *= 0.10;
	}
	*decexpp = decexp;			/* Store the decimal exponent */

	/* Compute the desired number of result digits. */
	if (fmt == 'e' || fmt == 'E')
		ndigits = prec + 1;		/* For 'e' or 'E' format */
	else if (fmt == 'f')
		ndigits = prec + decexp + 1;	/* For 'f' format */
	else
		ndigits = prec;			/* For 'g' or 'G' format */
	if (ndigits <= 0) {			/* No significant digits */
		if (ndigits == 0 && d > 5.0)	/* Round up to one digit */
			goto ret1;		/* and return "1" */
		else
			goto ret0;		/* return "0" */
	} else if (ndigits > DBL_DIG)
		ndigits = DBL_DIG;		/* Maximum precision */
	
	/* Compute the result digits. */
	for ( ; cp < &buf[ndigits] && d != 0.0; ) {
		digit = (int) d;
		*cp++ = digit + '0';		/* Store next digit */
		d = 10.0 * (d-digit);		/* and reduce d accordingly */
	}
	*cp = '\0';				/* NUL-terminate result */

	/* Round up the result if appropriate. */
	if (d <= 5.0) {				/* Do not round up */
		while (--cp != buf && *cp == '0')
			*cp = '\0';		/* Strip a trailing '0' */
		return;
	}
	while (cp-- != buf) {			/* Round up */
		if (++*cp <= '9')		/* Bump last digit */
			return;
		*cp = '\0';			/* Strip a trailing '0' */
	}
	++cp;					/* Point to buf again */
ret1:
	++*decexpp;				/* and return "1" */
	*cp++ = '1';
	*cp = '\0';
}

/* end of libc/stdio/_dtefg.c */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.