/* formater for floating point numbers, called by printf and friends */
/* written by Eric R. Smith */
/* Please write something better! */

#include <ctype.h>

/* find the mantissa and exponent of a floating point number */
/* this is similar to frexp, except we want to use base 10 instead of
   base 2 */
/* FIXME: there has GOT to be a better way to do this. */

/* Here is a (hopefully) new improved one, ++jrb */

/* bit struct of an ieee double */
struct bitdouble {	
	unsigned long sign  : 1;
	unsigned long exp   : 11;
	unsigned long mant1 : 20;
	unsigned long mant2;
};

static double decfrexp(value, exponent)
double value;
int *exponent;
{
	register struct bitdouble *p = (struct bitdouble *) &value;
	register unsigned short e;
	register short tene;
	
	tene = 0;
	e =  p->exp;

	if(e > 1025)
	{
		for(; p->exp >= 1027; tene++)
		    value /= 10.0;
		if((p->exp == 1026) && ( p->mant1 >= 0x40000))
		{
			value /= 10.0;
			tene++;
		}
	}
	else if(e < 1021)
	{
		for(; p->exp <= 1022; --tene)
		    value *= 10.0;
	} 
	/* else -- need to do nothing */

	*exponent = tene;
	return value;
}

/* format a floating point number */

void
fp_print(value, format, precision, buf)
	double 	value;		/* the number to format */
	int	format;		/* how to format it: really a char, for now */
	int	precision;	/* precision, or # of places after '.' */
	char	*buf;		/* buffer into which the result goes */
{
	int 	digit;
	int	exp;
	double  mantissa;
	int decpoint = 1;
	int useexp = 1;
	int ndigits;

	if(precision == -1) /* ++jrb, prec not spec, use default prec */
		precision = 6;

	if (value < 0.0) {
		*buf++ = '-';
		value = -value;
	}

	/* ++jrb   round off at prec + 1 */	
	for(mantissa = 0.5, ndigits = 0; ndigits < precision; ndigits++) 
		mantissa /= 10.0;
	value += mantissa;

	mantissa = decfrexp(value, &exp);
	switch(format) {
	   case 'e': case 'E':
		ndigits = precision + 1; /* one place in front of decimal */
		break;
	   case 'g': case 'G':
	   default:
		ndigits = precision;
              if (exp >= -3 && exp < precision)
                      useexp = 0;             /* use f format */
		break;
	   case 'f':
		if (exp < 0)
			ndigits = precision + 1;
		else
			ndigits = precision + exp + 1;
		useexp = 0;
		break;
	}

/* preliminaries for f format */
	if (!useexp) {
		if (exp < 0) {
			*buf++ = '0'; *buf++ = '.';
			ndigits--;
			while (++exp < 0 && ndigits > 0) {
				*buf++ = '0'; ndigits--;
			}
			decpoint = -1;
		}
		else {
			decpoint = exp + 1;
		}
	}

	while (ndigits > 0) {
		digit = (int) (mantissa);
		mantissa = (mantissa - (double) digit) * 10;
		*buf++ = digit + '0';
		if (--decpoint == 0)
			*buf++ = '.';
		--ndigits;
	};

	if (useexp) {
		*buf++ = (isupper(format)) ? 'E' : 'e';
		if (exp < 0) {
			*buf++ = '-';
			exp = -exp;
		}
		else
			*buf++ = '+';
		if((digit = exp/100) > 0) {
			*buf++ = digit + '0';
			exp %= 100;
		}
		*buf++ = (exp/10) + '0';
		*buf++ = (exp%10) + '0';
	}
	*buf++ = '\0';
}
