154359Sroberto/*
254359Sroberto * dolfptoa - do the grunge work of converting an l_fp number to decimal
354359Sroberto */
4290000Sglebius#include <config.h>
554359Sroberto#include <stdio.h>
654359Sroberto
754359Sroberto#include "ntp_fp.h"
854359Sroberto#include "lib_strbuf.h"
954359Sroberto#include "ntp_string.h"
1054359Sroberto#include "ntp_stdlib.h"
1154359Sroberto
1254359Srobertochar *
1354359Srobertodolfptoa(
14290000Sglebius	u_int32 fpi,
15290000Sglebius	u_int32 fpv,
1654359Sroberto	int neg,
17132451Sroberto	short ndec,
1854359Sroberto	int msec
1954359Sroberto	)
2054359Sroberto{
21290000Sglebius	u_char *cp, *cpend, *cpdec;
22290000Sglebius	int dec;
2354359Sroberto	u_char cbuf[24];
24290000Sglebius	char *buf, *bp;
2554359Sroberto
2654359Sroberto	/*
2754359Sroberto	 * Get a string buffer before starting
2854359Sroberto	 */
2954359Sroberto	LIB_GETBUF(buf);
3054359Sroberto
3154359Sroberto	/*
3254359Sroberto	 * Zero the character buffer
3354359Sroberto	 */
34290000Sglebius	ZERO(cbuf);
3554359Sroberto
3654359Sroberto	/*
37290000Sglebius	 * Work on the integral part. This should work reasonable on
38290000Sglebius	 * all machines with 32 bit arithmetic. Please note that 32 bits
39290000Sglebius	 * can *always* be represented with at most 10 decimal digits,
40290000Sglebius	 * including a possible rounding from the fractional part.
4154359Sroberto	 */
42290000Sglebius	cp = cpend = cpdec = &cbuf[10];
43293894Sglebius	for (dec = (int)(cp - cbuf); dec > 0 && fpi != 0; dec--) {
44290000Sglebius		/* can add another digit */
45290000Sglebius		u_int32 digit;
46290000Sglebius
47290000Sglebius		digit  = fpi;
48290000Sglebius		fpi   /= 10U;
49290000Sglebius		digit -= (fpi << 3) + (fpi << 1); /* i*10 */
50290000Sglebius		*--cp  = (u_char)digit;
5154359Sroberto	}
5254359Sroberto
5354359Sroberto	/*
5454359Sroberto	 * Done that, now deal with the problem of the fraction.  First
5554359Sroberto	 * determine the number of decimal places.
5654359Sroberto	 */
57290000Sglebius	dec = ndec;
58290000Sglebius	if (dec < 0)
59290000Sglebius		dec = 0;
6054359Sroberto	if (msec) {
61290000Sglebius		dec   += 3;
62290000Sglebius		cpdec += 3;
6354359Sroberto	}
64290000Sglebius	if ((size_t)dec > sizeof(cbuf) - (cpend - cbuf))
65293894Sglebius		dec = (int)(sizeof(cbuf) - (cpend - cbuf));
6654359Sroberto
6754359Sroberto	/*
6854359Sroberto	 * If there's a fraction to deal with, do so.
6954359Sroberto	 */
70290000Sglebius	for (/*NOP*/;  dec > 0 && fpv != 0;  dec--)  {
71290000Sglebius		u_int32 digit, tmph, tmpl;
72290000Sglebius
7354359Sroberto		/*
74290000Sglebius		 * The scheme here is to multiply the fraction
75290000Sglebius		 * (0.1234...) by ten.  This moves a junk of BCD into
76290000Sglebius		 * the units part.  record that and iterate.
77290000Sglebius		 * multiply by shift/add in two dwords.
7854359Sroberto		 */
79290000Sglebius		digit = 0;
80290000Sglebius		M_LSHIFT(digit, fpv);
81290000Sglebius		tmph = digit;
82290000Sglebius		tmpl = fpv;
83290000Sglebius		M_LSHIFT(digit, fpv);
84290000Sglebius		M_LSHIFT(digit, fpv);
85290000Sglebius		M_ADD(digit, fpv, tmph, tmpl);
86290000Sglebius		*cpend++ = (u_char)digit;
87290000Sglebius	}
8854359Sroberto
89290000Sglebius	/* decide whether to round or simply extend by zeros */
90290000Sglebius	if (dec > 0) {
91290000Sglebius		/* only '0' digits left -- just reposition end */
92290000Sglebius		cpend += dec;
93290000Sglebius	} else {
94290000Sglebius		/* some bits remain in 'fpv'; do round */
95290000Sglebius		u_char *tp    = cpend;
96290000Sglebius		int     carry = ((fpv & 0x80000000) != 0);
97290000Sglebius
98293894Sglebius		for (dec = (int)(tp - cbuf);  carry && dec > 0;  dec--) {
99290000Sglebius			*--tp += 1;
100290000Sglebius			if (*tp == 10)
10154359Sroberto				*tp = 0;
102290000Sglebius			else
103290000Sglebius				carry = FALSE;
10454359Sroberto		}
105290000Sglebius
106290000Sglebius		if (tp < cp) /* rounding from 999 to 1000 or similiar? */
107290000Sglebius			cp = tp;
10854359Sroberto	}
10954359Sroberto
11054359Sroberto	/*
11154359Sroberto	 * We've now got the fraction in cbuf[], with cp pointing at
11254359Sroberto	 * the first character, cpend pointing past the last, and
11354359Sroberto	 * cpdec pointing at the first character past the decimal.
11454359Sroberto	 * Remove leading zeros, then format the number into the
11554359Sroberto	 * buffer.
11654359Sroberto	 */
117290000Sglebius	while (cp < cpdec && *cp == 0)
11854359Sroberto		cp++;
119290000Sglebius	if (cp >= cpdec)
120290000Sglebius		cp = cpdec - 1;
12154359Sroberto
12254359Sroberto	bp = buf;
12354359Sroberto	if (neg)
124290000Sglebius		*bp++ = '-';
12554359Sroberto	while (cp < cpend) {
12654359Sroberto		if (cp == cpdec)
127290000Sglebius			*bp++ = '.';
128290000Sglebius		*bp++ = (char)(*cp++) + '0';
12954359Sroberto	}
13054359Sroberto	*bp = '\0';
13154359Sroberto
13254359Sroberto	/*
13354359Sroberto	 * Done!
13454359Sroberto	 */
13554359Sroberto	return buf;
13654359Sroberto}
137290000Sglebius
138290000Sglebius
139290000Sglebiuschar *
140290000Sglebiusmfptoa(
141290000Sglebius	u_int32	fpi,
142290000Sglebius	u_int32	fpf,
143290000Sglebius	short	ndec
144290000Sglebius	)
145290000Sglebius{
146290000Sglebius	int	isneg;
147290000Sglebius
148290000Sglebius	isneg = M_ISNEG(fpi);
149290000Sglebius	if (isneg) {
150290000Sglebius		M_NEG(fpi, fpf);
151290000Sglebius	}
152290000Sglebius
153290000Sglebius	return dolfptoa(fpi, fpf, isneg, ndec, FALSE);
154290000Sglebius}
155290000Sglebius
156290000Sglebius
157290000Sglebiuschar *
158290000Sglebiusmfptoms(
159290000Sglebius	u_int32	fpi,
160290000Sglebius	u_int32	fpf,
161290000Sglebius	short	ndec
162290000Sglebius	)
163290000Sglebius{
164290000Sglebius	int	isneg;
165290000Sglebius
166290000Sglebius	isneg = M_ISNEG(fpi);
167290000Sglebius	if (isneg) {
168290000Sglebius		M_NEG(fpi, fpf);
169290000Sglebius	}
170290000Sglebius
171290000Sglebius	return dolfptoa(fpi, fpf, isneg, ndec, TRUE);
172290000Sglebius}
173290000Sglebius
174290000Sglebius
175