154359Sroberto/*
254359Sroberto * dolfptoa - do the grunge work of converting an l_fp number to decimal
354359Sroberto */
454359Sroberto#include <stdio.h>
554359Sroberto
654359Sroberto#include "ntp_fp.h"
754359Sroberto#include "lib_strbuf.h"
854359Sroberto#include "ntp_string.h"
954359Sroberto#include "ntp_stdlib.h"
1054359Sroberto
1154359Srobertochar *
1254359Srobertodolfptoa(
1354359Sroberto	u_long fpi,
1454359Sroberto	u_long fpv,
1554359Sroberto	int neg,
16132451Sroberto	short ndec,
1754359Sroberto	int msec
1854359Sroberto	)
1954359Sroberto{
2054359Sroberto	register u_char *cp, *cpend;
2154359Sroberto	register u_long lwork;
2254359Sroberto	register int dec;
2354359Sroberto	u_char cbuf[24];
2454359Sroberto	u_char *cpdec;
2554359Sroberto	char *buf;
2654359Sroberto	char *bp;
2754359Sroberto
2854359Sroberto	/*
2954359Sroberto	 * Get a string buffer before starting
3054359Sroberto	 */
3154359Sroberto	LIB_GETBUF(buf);
3254359Sroberto
3354359Sroberto	/*
3454359Sroberto	 * Zero the character buffer
3554359Sroberto	 */
3654359Sroberto	memset((char *) cbuf, 0, sizeof(cbuf));
3754359Sroberto
3854359Sroberto	/*
39182007Sroberto	 * safeguard against sign extensions and other mishaps on 64 bit platforms
40182007Sroberto	 * the code following is designed for and only for 32-bit inputs and
41182007Sroberto	 * only 32-bit worth of input are supplied.
42182007Sroberto         */
43182007Sroberto	fpi &= 0xffffffff;
44182007Sroberto	fpv &= 0xffffffff;
45182007Sroberto
46182007Sroberto	/*
4754359Sroberto	 * Work on the integral part.  This is biased by what I know
4854359Sroberto	 * compiles fairly well for a 68000.
4954359Sroberto	 */
5054359Sroberto	cp = cpend = &cbuf[10];
5154359Sroberto	lwork = fpi;
5254359Sroberto	if (lwork & 0xffff0000) {
5354359Sroberto		register u_long lten = 10;
5454359Sroberto		register u_long ltmp;
5554359Sroberto
5654359Sroberto		do {
5754359Sroberto			ltmp = lwork;
5854359Sroberto			lwork /= lten;
5954359Sroberto			ltmp -= (lwork << 3) + (lwork << 1);
60182007Sroberto			if (cp < cbuf) abort(); /* rather die a horrible death than trash the memory */
6154359Sroberto			*--cp = (u_char)ltmp;
6254359Sroberto		} while (lwork & 0xffff0000);
6354359Sroberto	}
6454359Sroberto	if (lwork != 0) {
6554359Sroberto		register u_short sten = 10;
6654359Sroberto		register u_short stmp;
6754359Sroberto		register u_short swork = (u_short)lwork;
6854359Sroberto
6954359Sroberto		do {
7054359Sroberto			stmp = swork;
71132451Sroberto			swork = (u_short) (swork/sten);
72132451Sroberto			stmp = (u_short)(stmp - ((swork<<3) + (swork<<1)));
73182007Sroberto		        if (cp < cbuf) abort(); /* rather die a horrible death than trash the memory */
7454359Sroberto			*--cp = (u_char)stmp;
7554359Sroberto		} while (swork != 0);
7654359Sroberto	}
7754359Sroberto
7854359Sroberto	/*
7954359Sroberto	 * Done that, now deal with the problem of the fraction.  First
8054359Sroberto	 * determine the number of decimal places.
8154359Sroberto	 */
8254359Sroberto	if (msec) {
8354359Sroberto		dec = ndec + 3;
8454359Sroberto		if (dec < 3)
8554359Sroberto		    dec = 3;
8654359Sroberto		cpdec = &cbuf[13];
8754359Sroberto	} else {
8854359Sroberto		dec = ndec;
8954359Sroberto		if (dec < 0)
9054359Sroberto		    dec = 0;
9154359Sroberto		cpdec = &cbuf[10];
9254359Sroberto	}
9354359Sroberto	if (dec > 12)
9454359Sroberto	    dec = 12;
9554359Sroberto
9654359Sroberto	/*
9754359Sroberto	 * If there's a fraction to deal with, do so.
9854359Sroberto	 */
9954359Sroberto	if (fpv != 0) {
10054359Sroberto		l_fp work;
10154359Sroberto
10254359Sroberto		work.l_ui = 0;
10354359Sroberto		work.l_uf = fpv;
10454359Sroberto		while (dec > 0) {
10554359Sroberto			l_fp ftmp;
10654359Sroberto
10754359Sroberto			dec--;
10854359Sroberto			/*
10954359Sroberto			 * The scheme here is to multiply the
11054359Sroberto			 * fraction (0.1234...) by ten.  This moves
11154359Sroberto			 * a junk of BCD into the units part.
11254359Sroberto			 * record that and iterate.
11354359Sroberto			 */
11454359Sroberto			work.l_ui = 0;
11554359Sroberto			L_LSHIFT(&work);
11654359Sroberto			ftmp = work;
11754359Sroberto			L_LSHIFT(&work);
11854359Sroberto			L_LSHIFT(&work);
11954359Sroberto			L_ADD(&work, &ftmp);
12054359Sroberto			*cpend++ = (u_char)work.l_ui;
12154359Sroberto			if (work.l_uf == 0)
12254359Sroberto			    break;
123182007Sroberto			if (cpend > (cbuf + sizeof(cbuf))) abort(); /* rather die a horrible death than trash the memory */
12454359Sroberto		}
12554359Sroberto
12654359Sroberto		/*
12754359Sroberto		 * Rounding is rotten
12854359Sroberto		 */
12954359Sroberto		if (work.l_uf & 0x80000000) {
13054359Sroberto			register u_char *tp = cpend;
13154359Sroberto
13254359Sroberto			*(--tp) += 1;
13354359Sroberto			while (*tp >= 10) {
13454359Sroberto				*tp = 0;
13554359Sroberto				*(--tp) += 1;
13654359Sroberto			};
13754359Sroberto			if (tp < cp)
13854359Sroberto			    cp = tp;
13954359Sroberto		}
14054359Sroberto	}
14154359Sroberto	cpend += dec;
14254359Sroberto
14354359Sroberto
14454359Sroberto	/*
14554359Sroberto	 * We've now got the fraction in cbuf[], with cp pointing at
14654359Sroberto	 * the first character, cpend pointing past the last, and
14754359Sroberto	 * cpdec pointing at the first character past the decimal.
14854359Sroberto	 * Remove leading zeros, then format the number into the
14954359Sroberto	 * buffer.
15054359Sroberto	 */
15154359Sroberto	while (cp < cpdec) {
15254359Sroberto		if (*cp != 0)
15354359Sroberto		    break;
15454359Sroberto		cp++;
15554359Sroberto	}
15654359Sroberto	if (cp == cpdec)
15754359Sroberto	    --cp;
15854359Sroberto
15954359Sroberto	bp = buf;
16054359Sroberto	if (neg)
16154359Sroberto	    *bp++ = '-';
16254359Sroberto	while (cp < cpend) {
16354359Sroberto		if (cp == cpdec)
16454359Sroberto		    *bp++ = '.';
16554359Sroberto		*bp++ = (char)(*cp++ + '0');	/* ascii dependent? */
16654359Sroberto	}
16754359Sroberto	*bp = '\0';
16854359Sroberto
16954359Sroberto	/*
17054359Sroberto	 * Done!
17154359Sroberto	 */
17254359Sroberto	return buf;
17354359Sroberto}
174