#if !defined(lint) && defined(SCCSIDS)
static char     sccsid[] = "@(#)double_decim.c 1.1 92/07/30 SMI";
#endif

/*
 * Copyright (c) 1988 by Sun Microsystems, Inc.
 */

/* Conversion between binary and decimal floating point. */

#include "base_conversion.h"

/* PRIVATE FUNCTIONS */

void
decimal_round(pm, pd, ps, round, sticky)
	decimal_mode   *pm;
	decimal_record *pd;
	fp_exception_field_type *ps;
	char            round;
	unsigned        sticky;

/*
 * Rounds decimal record *pd according to modes in *pm, recording exceptions
 * for inexact or overflow in *ps.  round is the round digit and sticky is 0
 * or non-zero to indicate exact or inexact. pd->ndigits is expected to be
 * correctly set.
 */

{
	int             lsd, i;

	if ((round == '0') && (sticky == 0)) {	/* Exact. */
		goto done;
	}
	*ps |= 1 << fp_inexact;

	switch (pm->rd) {
	case fp_nearest:
		if (round < '5')
			goto done;
		if (round > '5')
			goto roundup;
		if (sticky != 0)
			goto roundup;
		/* Now in ambiguous case; round up if lsd is odd. */
		if (pd->ndigits <= 0)
			goto done;	/* Presumed 0. */
		lsd = pd->ds[pd->ndigits - 1] - '0';
		if ((lsd % 2) == 0)
			goto done;
		goto roundup;
	case fp_positive:
		if (pd->sign != 0)
			goto done;
		goto roundup;
	case fp_negative:
		if (pd->sign == 0)
			goto done;
		goto roundup;
	case fp_tozero:
		goto done;
	}
roundup:
	for (i = (pd->ndigits - 1); (pd->ds[i] == '9') && (i >= 0); i--)
		pd->ds[i] = '0';
	if (i >= 0)
		(pd->ds[i])++;
	else {			/* Rounding carry out has occurred. */
		pd->ds[0] = '1';
		if (pm->df == floating_form) {	/* For E format, simply
						 * adjust exponent. */
			pd->exponent++;
		} else {	/* For F format, increase length of string. */
			if (pd->ndigits > 0)
				pd->ds[pd->ndigits] = '0';
			pd->ndigits++;
		}
	}
	goto ret;
done:
	if (pd->ndigits <= 0) {	/* Create zero string. */
		pd->ds[0] = '0';
		pd->ndigits = 1;
	}
ret:
	pd->ds[pd->ndigits] = 0;/* Terminate string. */
	return;
}

void
binary_to_decimal_integer(pu, nsig, ds, nzeros, ndigs)
	unpacked       *pu;	/* Input unpacked integer value input. */
	unsigned        nsig;	/* Input number of significant digits
				 * required. */
	char            ds[];	/* Output decimal integer string output -
				 * must be large enough. */
unsigned       *nzeros;		/* Output number of implicit trailing zeros
				 * produced. */
unsigned       *ndigs;		/* Output number of explicit digits produced
				 * in ds. */

/*
 * Converts an unpacked integer value *pu into a decimal string in *ds, of
 * length returned in *ndigs. /* Inexactness is indicated by setting
 * ds[ndigs-1] odd.
 */

{

	_big_float     *pd, b, d;
	int             e, i, is;
	_BIG_FLOAT_DIGIT stickyshift;
	char            s[4];

	b.bsize = _BIG_FLOAT_SIZE;	/* Initialize sizes of big floats. */
	d.bsize = _BIG_FLOAT_SIZE;
	_unpacked_to_big_float(pu, &b, &e);
	if (e < 0) {
		_right_shift_base_two(&b, (short unsigned) -e, &stickyshift);
#ifdef DEBUG
		assert(stickyshift == 0);
#endif
	}
	_big_binary_to_big_decimal(&b, &d);
	if (e <= 0)
		pd = &d;
	else {
		_big_float_times_power(&d, 2, e, (int) nsig, &pd);
		switch (pd) {
		case BIG_FLOAT_TIMES_TOOBIG:
			{
				char            bcastring[80];

				(void) sprintf(bcastring, " binary exponent %d ", e);
				_base_conversion_abort(ERANGE, bcastring);
				break;
			}
		case BIG_FLOAT_TIMES_NOMEM:
			{
				char            bcastring[80];

				(void) sprintf(bcastring, " binary exponent %d ", e);
				_base_conversion_abort(ENOMEM, bcastring);
				break;
			}
		default:
#ifdef DEBUG
			if (pd != &d)
				(void) printf(" large binary exponent %d needs heap buffer \n", e);
			printf(" product ");
			_display_big_float(pd, 10);
#endif
			break;
		}
	}
	_fourdigitsquick((short unsigned) pd->bsignificand[pd->blength - 1], s);
	for (i = 0; s[i] == '0'; i++);	/* Find first non-zero digit. */
	for (is = 0; i <= 3;)
		ds[is++] = s[i++];	/* Copy subsequent digits. */

	for (i = (pd->blength - 2); i >= 0; i--) {	/* Convert powers of
							 * 10**4 to decimal
							 * digits. */
		_fourdigitsquick((short unsigned) pd->bsignificand[i], &(ds[is]));
		is += 4;
	}

	ds[is] = 0;
	*ndigs = is;
	*nzeros = pd->bexponent;
	if (pd != &d)
		_free_big_float(pd);

#ifdef DEBUG
	printf(" binary to decimal integer result %s * 10**%d \n", ds, *nzeros);
#endif
}

void
binary_to_decimal_fraction(pu, nsig, nfrac, ds, nzeros, ndigs)
	unpacked       *pu;	/* Input unpacked fraction value output < 1
				 * in magnitude. */
	unsigned        nsig;	/* Input number of significant digits
				 * required. */
	unsigned        nfrac;	/* Input number of digits after point
				 * required. */
	char            ds[];	/* Output decimal integer string output -
				 * must be large enough. */
int            *nzeros;		/* Output number of implicit leading zeros
				 * produced. */
int            *ndigs;		/* Output number of explicit digits produced
				 * in ds. */

/*
 * Converts an unpacked fraction value *pu into a decimal string consisting
 * of a) an implicit '.' b) *nzeros implicit leading zeros c) *ndigs explicit
 * digits in ds ds contains at least nsig significant digits. nzeros + *
 * *ndigs is at least nfrac digits after the point. Inexactness is indicated
 * by sticking to the lsb.
 */

{
	_big_float     *pb, b, d;
	int             e, i, j, is, excess;
	char            s[4];
	int             tensig, tenpower;
	_BIG_FLOAT_DIGIT stickyshift;

	*nzeros = 0;
	if (pu->fpclass == fp_zero) {	/* Exact zero. */
		for (i = 0; i <= nfrac; i++)
			ds[i] = '0';
		for (; i <= nsig; i++)
			ds[i] = '0';
		*ndigs = i;
		return;
	}
	b.bsize = _BIG_FLOAT_SIZE;	/* Initialize sizes of big floats. */
	d.bsize = _BIG_FLOAT_SIZE;
	_unpacked_to_big_float(pu, &b, &e);
	/*
	 * e < 0 always
	 */
	b.bexponent = e;
	tenpower = nsig + (int) (((17 - e - 16 * b.blength) * (unsigned long) 19729) >> 16);
	if (tenpower < nfrac)
		tenpower = nfrac;
	tensig = nfrac;
	if (nsig > tensig)
		tensig = nsig;
	tensig = 1 + (((tensig + 2) * 217706) >> 16);
	tensig = -tensig;

#ifdef DEBUG
	printf(" binary to decimal fraction exponent 2**%d \n", e);
	printf(" binary to decimal fraction nsig %d nfrac %d tenpower %d tensig %d \n", nsig, nfrac, tenpower, tensig);
#endif
	_big_float_times_power(&b, 10, tenpower, tensig, &pb);
	switch (pb) {
	case BIG_FLOAT_TIMES_TOOBIG:
		{
			char            bcastring[80];

			(void) sprintf(bcastring, " decimal exponent %d ", tenpower);
			_base_conversion_abort(ERANGE, bcastring);
			break;
		}
	case BIG_FLOAT_TIMES_NOMEM:
		{
			char            bcastring[80];

			(void) sprintf(bcastring, " decimal exponent %d ", tenpower);
			_base_conversion_abort(ENOMEM, bcastring);
			break;
		}
	default:
#ifdef DEBUG
		if (pb != &b)
			printf(" large decimal exponent %d needs heap buffer \n", tenpower);
		printf(" product ");
		_display_big_float(pb, 2);
#endif
		break;
	}

	if (pb->bexponent <= -16) {
		/* Have computed appropriate decimal part; now toss fraction. */
		excess = (-pb->bexponent) / 16;
#ifdef DEBUG
		printf(" discard %d excess fraction bits \n", 16 * excess);
#endif
		for (i = 0; (i < excess) && (pb->bsignificand[i] == 0); i++);
		if (i < excess)
			pb->bsignificand[excess] |= 1;	/* Sticky bit for
							 * discarded fraction. */
		for (i = excess; i < pb->blength; i++)
			pb->bsignificand[i - excess] = pb->bsignificand[i];

		pb->blength -= excess;
		pb->bexponent += 16 * excess;
	}
	if (pb->bexponent < 0) {
		_right_shift_base_two(pb, (short unsigned) -pb->bexponent, &stickyshift);
		if (stickyshift != 0)
			pb->bsignificand[0] |= 1;	/* Stick to lsb. */
	}
	_big_binary_to_big_decimal(pb, &d);

	i = d.blength - 1;
	while (d.bsignificand[i] == 0)
		i--;
	_fourdigitsquick((short unsigned) d.bsignificand[i], s);
	for (j = 0; s[j] == '0'; j++);	/* Find first non-zero digit. */
	for (is = 0; j <= 3;)
		ds[is++] = s[j++];	/* Copy subsequent digits. */

	for (i--; i >= 0; i--) {/* Convert powers of 10**4 to decimal digits. */
		_fourdigitsquick((short unsigned) d.bsignificand[i], &(ds[is]));
		is += 4;
	}

	ds[is] = 0;
	*ndigs = is;
#ifdef DEBUG
	assert(tenpower >= is);
#endif
	*nzeros = tenpower - is;/* There were supposed to be tenpower leading
				 * digits, and is were found. */

	if (pb != &b)
		_free_big_float(pb);

#ifdef DEBUG
	printf(" binary to decimal fraction result .%s * 10**%d \n", ds, -(*nzeros));
#endif

}

void
_unpacked_to_decimal(px, pm, pd, ps)
	unpacked       *px;
	decimal_mode   *pm;
	decimal_record *pd;
	fp_exception_field_type *ps;
{
	unpacked        fx, ix;
	unsigned        fmask, imask;
	int             i, intdigs, fracdigs, fraczeros, fracsigs, ids, idsbound, lzbound;
	unsigned        nsig, nfrac, intzeros, intsigs;
	char            is[_INTEGER_SIZE], fs[DECIMAL_STRING_LENGTH];
	char            round = '0';
	unsigned        sticky = 0;

	pd->sign = px->sign;
	pd->fpclass = px->fpclass;
	if ((px->fpclass != fp_normal) && (px->fpclass != fp_subnormal))
		return;
	if ((pm->ndigits >= DECIMAL_STRING_LENGTH) ||
	    ((pm->df == floating_form) && (pm->ndigits < 1))) {	/* Gross overflow or bad
								 * spec. */
overflow:
		*ps |= 1 << fp_overflow;
		return;
	}
	/* Divide x up into integer part ix and fraction part fx.	 */

	ix = *px;
	fx = ix;
	if (ix.exponent <= -1) {/* All fraction. */
		ix.fpclass = fp_zero;
	} else if (ix.exponent >= 159) {	/* All integer. */
		fx.fpclass = fp_zero;
	} else if ((ix.exponent % 32) == 31) {	/* Integer/fraction boundary
						 * is conveniently on a word
						 * boundary. */
		imask = (ix.exponent + 1) / 32;	/* Words 0..imask-1 are
						 * integer; imask..SIZE are
						 * fraction. */
		for (i = 0; i < imask; i++)
			fx.significand[i] = 0;
		for (; i < UNPACKED_SIZE; i++)
			ix.significand[i] = 0;
		_fp_normalize(&fx);
	} else {		/* Integer/fraction boundary falls in the
				 * middle of a word. */
		imask = (ix.exponent + 1) / 32;	/* Words 0..imask-1 are
						 * integer; imask is integer
						 * and fraction ;
						 * imask+1..SIZE are
						 * fraction. */
		for (i = 0; i < imask; i++)
			fx.significand[i] = 0;
		fmask = (1 << (31 - (ix.exponent % 32))) - 1;
		fx.significand[imask] &= fmask;
		ix.significand[imask] &= ~fmask;
		for (i = (imask + 1); i < UNPACKED_SIZE; i++)
			ix.significand[i] = 0;
		_fp_normalize(&fx);
	}
	if (ix.fpclass != fp_zero) {	/* Compute integer part of result. */
		if (pm->df == floating_form)
			nsig = pm->ndigits + 1;	/* Significant digits wanted
						 * for E format, plus one for
						 * rounding. */
		else
			nsig = _INTEGER_SIZE;	/* Significant digits wanted
						 * for F format == all. */

		binary_to_decimal_integer(&ix, nsig, is, &intzeros, &intsigs);
	} else {
		intsigs = 0;
		intzeros = 0;
	}
	intdigs = intsigs + intzeros;
	fracdigs = 0;
	if (((pm->df == fixed_form) && (pm->ndigits >= 0)) ||
	    ((pm->df == floating_form) && ((pm->ndigits + 1) > intdigs))) {	/* Need to compute
										 * fraction part. */
		if (pm->df == floating_form) {	/* Need more significant
						 * digits. */
			nsig = pm->ndigits + 2 - intdigs;	/* Add two for rounding,
								 * sticky. */
			if (nsig > DECIMAL_STRING_LENGTH)
				nsig = DECIMAL_STRING_LENGTH;
			nfrac = 1;
		} else {	/* Need fraction digits. */
			nsig = 0;
			nfrac = pm->ndigits + 2;	/* Add two for rounding,
							 * sticky. */
			if (nfrac > DECIMAL_STRING_LENGTH)
				nfrac = DECIMAL_STRING_LENGTH;
		}
		binary_to_decimal_fraction(&fx, nsig, nfrac, fs, &fraczeros, &fracsigs);
		fracdigs = fraczeros + fracsigs;
	}
	if (pm->df == floating_form) {	/* Combine integer and fraction for E
					 * format. */
		idsbound = intsigs;
		if (idsbound > pm->ndigits)
			idsbound = pm->ndigits;
		for (ids = 0; ids < idsbound; ids++)
			pd->ds[ids] = is[ids];
		/* Put integer into output string. */
		idsbound = intsigs + intzeros;
		if (idsbound > pm->ndigits)
			idsbound = pm->ndigits;
		for (; ids < idsbound; ids++)
			pd->ds[ids] = '0';
		if (ids == pm->ndigits) {	/* Integer part had enough
						 * significant digits. */
			pd->ndigits = ids;
			pd->exponent = intdigs - ids;
			if (ids < intdigs) {	/* Gather rounding info. */
				if (ids < intsigs)
					round = is[ids++];
				else
					round = '0';
				for (; (is[ids] == '0') && (ids < intsigs); ids++);
				if (ids < intsigs)
					sticky = 1;
				if (fx.fpclass != fp_zero)
					sticky = 1;
			} else {/* Integer part is exact - round from
				 * fraction. */
				if (fx.fpclass != fp_zero) {
					int             stickystart;
					/* Fraction non-zero. */
					if (fraczeros > 0) {	/* Round digit is zero. */
						round = '0';
						stickystart = 0;	/* Stickies start with
									 * fs[0]. */
					} else {	/* Round digit is fs[0]. */
						round = fs[0];
						stickystart = 1;	/* Stickies start with
									 * fs[1]. */
					}
					if (sticky == 0) {	/* Search for sticky
								 * bits. */
						for (ids = stickystart; (fs[ids] == '0') && (ids < fracdigs); ids++);
						if (ids < fracdigs)
							sticky = 1;
					}
				}
			}
		} else {	/* Need more significant digits from fraction
				 * part. */
			idsbound = pm->ndigits - ids;
			if (ids == 0) {	/* No integer part - find first
					 * significant digit. */
				for (i = 0; fs[i] == '0'; i++);
				idsbound = i + idsbound + fraczeros;
				i += fraczeros;	/* Point i at first
						 * significant digit. */
			} else
				i = 0;
			if (idsbound > fracdigs)
				idsbound = fracdigs;
			pd->exponent = -idsbound;

			if (fraczeros < idsbound)	/* Compute number of
							 * leading zeros
							 * required. */
				lzbound = fraczeros;
			else
				lzbound = idsbound;
			for (; (i < lzbound); i++)
				pd->ds[ids++] = '0';
			for (; (i < idsbound); i++)
				pd->ds[ids++] = fs[i - fraczeros];
			i -= fraczeros;	/* Don't worry about leading zeros
					 * from now on, we're just rounding */
			if (i < fracsigs) {	/* Gather rounding info.  */
				if (i < 0)
					round = '0';
				else
					round = fs[i];
				i++;
				if (sticky == 0) {	/* Find out if remainder
							 * is exact. */
					if (i < 0)
						i = 0;
					for (; (fs[i] == '0') && (i < fracsigs); i++);
					if (i < fracsigs)
						sticky = 1;
				}
			} else {/* Fraction part is exact - add zero digits
				 * if required. */
				for (; ids < pm->ndigits; ids++)
					pd->ds[ids] = '0';
			}
			pd->ndigits = ids;
		}
		decimal_round(pm, pd, ps, round, sticky);
	} else {		/* Combine integer and fraction for F format. */
		if (pm->ndigits >= 0) {	/* Normal F format. */
			if ((intdigs + pm->ndigits) >= DECIMAL_STRING_LENGTH)
				goto overflow;
			for (ids = 0; ids < intsigs; ids++)
				pd->ds[ids] = is[ids];
			for (; ids < intdigs; ids++)
				pd->ds[ids] = '0';
			/* Copy integer digits. */
			idsbound = fracdigs;
			if (idsbound > pm->ndigits)
				idsbound = pm->ndigits;
			if (fraczeros < idsbound)	/* Compute number of
							 * leading zeros
							 * required. */
				lzbound = fraczeros;
			else
				lzbound = idsbound;
			for (i = 0; (i < lzbound); i++)
				pd->ds[ids++] = '0';
			for (; (i < idsbound); i++)
				pd->ds[ids++] = fs[i - fraczeros];	/* Copy fraction digits. */
			for (; i < pm->ndigits; i++)
				pd->ds[ids++] = '0';
			/* Copy trailing zeros if necessary. */
			pd->ndigits = ids;
			pd->exponent = intdigs - ids;
			i -= fraczeros;	/* Don't worry about leading zeros
					 * from now on, we're just rounding */
			if (i < fracsigs) {	/* Gather rounding info.  */
				if (i < 0)
					round = '0';
				else
					round = fs[i];
				i++;
				if (sticky == 0) {	/* Find out if remainder
							 * is exact. */
					if (i < 0)
						i = 0;
					for (; (fs[i] == '0') && (i < fracsigs); i++);
					if (i < fracsigs)
						sticky = 1;
				}
			}
			decimal_round(pm, pd, ps, round, sticky);
		} else {	/* Bizarre F format - round to left of point. */
			int             roundpos = -pm->ndigits;

			if (intdigs >= DECIMAL_STRING_LENGTH)
				goto overflow;
			if (roundpos >= DECIMAL_STRING_LENGTH)
				goto overflow;
			if (intdigs <= roundpos) {	/* Not enough integer
							 * digits. */
				if (intdigs == roundpos) {
					round = is[0];
					i = 1;
				} else {
					round = '0';
					i = 0;
				}
				for (; (is[i] == '0') && (i < intsigs); i++);
				/* Search for sticky bits. */
				if (i < intsigs)
					sticky = 1;
				pd->ndigits = 0;
			} else {/* Some integer digits do not get rounded
				 * away. */
#ifdef _NO_GOOD
				for (ids = 0; ids < (intsigs - roundpos); ids++)
					pd->ds[ids] = is[ids];
				for (ids = 0; ids < (intdigs - roundpos); ids++)
					pd->ds[ids] = '0';
#else
                                 {
                                         int             ncopy = intsigs - roundpos;
                                         if (ncopy > 0) {
                                                 /* Copy integer digits. */
                                                 (void) memcpy(&(pd->ds[0]), &(is[0]), ncopy);
                                                 ids = ncopy;
                                         }
                                 }
                                 {
                                         int             ncopy = intdigs - roundpos - ids ;
                                         if (ncopy > 0) {
                                                 (void) memset(&(pd->ds[ids]), '0', ncopy);
                                                 ids += ncopy;
                                         }
                                 }
#endif /* _NO_GOOD */
				/* Copy integer digits. */
				pd->ndigits = ids;
				if (ids < intsigs) {	/* Inexact. */
					round = is[ids++];
					for (; (is[ids] == '0') && (ids < intsigs); ids++);
					/* Search for non-zero digits. */
					if (ids < intsigs)
						sticky = 1;
				}
			}
			if (fx.fpclass != fp_zero)
				sticky = 1;
			decimal_round(pm, pd, ps, round, sticky);
			for (i = pd->ndigits; i < (pd->ndigits + roundpos); i++)
				pd->ds[i] = '0';	/* Blank out rounded
							 * away digits. */
			pd->exponent = 0;
			pd->ndigits = i;
			pd->ds[i] = 0;	/* Terminate string. */
		}
	}
}

void
double_to_decimal(px, pm, pd, ps)
	double         *px;
	decimal_mode   *pm;
	decimal_record *pd;
	fp_exception_field_type *ps;
{
	double_equivalence kluge;
	unpacked        u;

	*ps = 0;		/* Initialize *ps. */
	kluge.x = *px;
	pd->sign = kluge.f.msw.sign;
	pd->fpclass = _class_double(px);
	switch (pd->fpclass) {
	case fp_zero:
		break;
	case fp_infinity:
		break;
	case fp_quiet:
		break;
	case fp_signaling:
		break;
	default:
		_unpack_double(&u, &kluge.x);
		_unpacked_to_decimal(&u, pm, pd, ps);
	}
}

void
quadruple_to_decimal(px, pm, pd, ps)
	quadruple      *px;
	decimal_mode   *pm;
	decimal_record *pd;
	fp_exception_field_type *ps;
{
	quadruple_equivalence kluge;
	unpacked        u;
	int             i;

	*ps = 0;		/* Initialize *ps - no exceptions. */
	for (i = 0; i < 4; i++)
		kluge.x.u[i] = px->u[i];
	pd->sign = kluge.f.msw.sign;
	pd->fpclass = _class_quadruple(px);
	switch (pd->fpclass) {
	case fp_zero:
		break;
	case fp_infinity:
		break;
	case fp_quiet:
		break;
	case fp_signaling:
		break;
	default:
		_unpack_quadruple(&u, px);
		_unpacked_to_decimal(&u, pm, pd, ps);
	}
}
