154359Sroberto/*
254359Sroberto * dolfptoa - do the grunge work of converting an l_fp number to decimal
354359Sroberto */
4290001Sglebius#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(
14290001Sglebius	u_int32 fpi,
15290001Sglebius	u_int32 fpv,
1654359Sroberto	int neg,
17132451Sroberto	short ndec,
1854359Sroberto	int msec
1954359Sroberto	)
2054359Sroberto{
21290001Sglebius	u_char *cp, *cpend, *cpdec;
22290001Sglebius	int dec;
2354359Sroberto	u_char cbuf[24];
24290001Sglebius	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	 */
34290001Sglebius	ZERO(cbuf);
3554359Sroberto
3654359Sroberto	/*
37290001Sglebius	 * Work on the integral part. This should work reasonable on
38290001Sglebius	 * all machines with 32 bit arithmetic. Please note that 32 bits
39290001Sglebius	 * can *always* be represented with at most 10 decimal digits,
40290001Sglebius	 * including a possible rounding from the fractional part.
4154359Sroberto	 */
42290001Sglebius	cp = cpend = cpdec = &cbuf[10];
43293896Sglebius	for (dec = (int)(cp - cbuf); dec > 0 && fpi != 0; dec--) {
44290001Sglebius		/* can add another digit */
45290001Sglebius		u_int32 digit;
46290001Sglebius
47290001Sglebius		digit  = fpi;
48290001Sglebius		fpi   /= 10U;
49290001Sglebius		digit -= (fpi << 3) + (fpi << 1); /* i*10 */
50290001Sglebius		*--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	 */
57290001Sglebius	dec = ndec;
58290001Sglebius	if (dec < 0)
59290001Sglebius		dec = 0;
6054359Sroberto	if (msec) {
61290001Sglebius		dec   += 3;
62290001Sglebius		cpdec += 3;
6354359Sroberto	}
64290001Sglebius	if ((size_t)dec > sizeof(cbuf) - (cpend - cbuf))
65293896Sglebius		dec = (int)(sizeof(cbuf) - (cpend - cbuf));
6654359Sroberto
6754359Sroberto	/*
6854359Sroberto	 * If there's a fraction to deal with, do so.
6954359Sroberto	 */
70290001Sglebius	for (/*NOP*/;  dec > 0 && fpv != 0;  dec--)  {
71290001Sglebius		u_int32 digit, tmph, tmpl;
72290001Sglebius
7354359Sroberto		/*
74290001Sglebius		 * The scheme here is to multiply the fraction
75290001Sglebius		 * (0.1234...) by ten.  This moves a junk of BCD into
76290001Sglebius		 * the units part.  record that and iterate.
77290001Sglebius		 * multiply by shift/add in two dwords.
7854359Sroberto		 */
79290001Sglebius		digit = 0;
80290001Sglebius		M_LSHIFT(digit, fpv);
81290001Sglebius		tmph = digit;
82290001Sglebius		tmpl = fpv;
83290001Sglebius		M_LSHIFT(digit, fpv);
84290001Sglebius		M_LSHIFT(digit, fpv);
85290001Sglebius		M_ADD(digit, fpv, tmph, tmpl);
86290001Sglebius		*cpend++ = (u_char)digit;
87290001Sglebius	}
8854359Sroberto
89290001Sglebius	/* decide whether to round or simply extend by zeros */
90290001Sglebius	if (dec > 0) {
91290001Sglebius		/* only '0' digits left -- just reposition end */
92290001Sglebius		cpend += dec;
93290001Sglebius	} else {
94290001Sglebius		/* some bits remain in 'fpv'; do round */
95290001Sglebius		u_char *tp    = cpend;
96290001Sglebius		int     carry = ((fpv & 0x80000000) != 0);
97290001Sglebius
98293896Sglebius		for (dec = (int)(tp - cbuf);  carry && dec > 0;  dec--) {
99290001Sglebius			*--tp += 1;
100290001Sglebius			if (*tp == 10)
10154359Sroberto				*tp = 0;
102290001Sglebius			else
103290001Sglebius				carry = FALSE;
10454359Sroberto		}
105290001Sglebius
106290001Sglebius		if (tp < cp) /* rounding from 999 to 1000 or similiar? */
107290001Sglebius			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	 */
117290001Sglebius	while (cp < cpdec && *cp == 0)
11854359Sroberto		cp++;
119290001Sglebius	if (cp >= cpdec)
120290001Sglebius		cp = cpdec - 1;
12154359Sroberto
12254359Sroberto	bp = buf;
12354359Sroberto	if (neg)
124290001Sglebius		*bp++ = '-';
12554359Sroberto	while (cp < cpend) {
12654359Sroberto		if (cp == cpdec)
127290001Sglebius			*bp++ = '.';
128290001Sglebius		*bp++ = (char)(*cp++) + '0';
12954359Sroberto	}
13054359Sroberto	*bp = '\0';
13154359Sroberto
13254359Sroberto	/*
13354359Sroberto	 * Done!
13454359Sroberto	 */
13554359Sroberto	return buf;
13654359Sroberto}
137290001Sglebius
138290001Sglebius
139290001Sglebiuschar *
140290001Sglebiusmfptoa(
141290001Sglebius	u_int32	fpi,
142290001Sglebius	u_int32	fpf,
143290001Sglebius	short	ndec
144290001Sglebius	)
145290001Sglebius{
146290001Sglebius	int	isneg;
147290001Sglebius
148290001Sglebius	isneg = M_ISNEG(fpi);
149290001Sglebius	if (isneg) {
150290001Sglebius		M_NEG(fpi, fpf);
151290001Sglebius	}
152290001Sglebius
153290001Sglebius	return dolfptoa(fpi, fpf, isneg, ndec, FALSE);
154290001Sglebius}
155290001Sglebius
156290001Sglebius
157290001Sglebiuschar *
158290001Sglebiusmfptoms(
159290001Sglebius	u_int32	fpi,
160290001Sglebius	u_int32	fpf,
161290001Sglebius	short	ndec
162290001Sglebius	)
163290001Sglebius{
164290001Sglebius	int	isneg;
165290001Sglebius
166290001Sglebius	isneg = M_ISNEG(fpi);
167290001Sglebius	if (isneg) {
168290001Sglebius		M_NEG(fpi, fpf);
169290001Sglebius	}
170290001Sglebius
171290001Sglebius	return dolfptoa(fpi, fpf, isneg, ndec, TRUE);
172290001Sglebius}
173290001Sglebius
174290001Sglebius
175