1/*
2 * dolfptoa - do the grunge work of converting an l_fp number to decimal
3 */
4#include <config.h>
5#include <stdio.h>
6
7#include "ntp_fp.h"
8#include "lib_strbuf.h"
9#include "ntp_string.h"
10#include "ntp_stdlib.h"
11
12char *
13dolfptoa(
14	u_int32 fpi,
15	u_int32 fpv,
16	char sign,
17	short ndec,
18	int msec
19	)
20{
21	u_char *cp, *cpend, *cpdec;
22	int dec;
23	u_char cbuf[24];
24	char *buf, *bp;
25
26	/*
27	 * Get a string buffer before starting
28	 */
29	LIB_GETBUF(buf);
30
31	/*
32	 * Zero the character buffer
33	 */
34	ZERO(cbuf);
35
36	/*
37	 * Work on the integral part. This should work reasonable on
38	 * all machines with 32 bit arithmetic. Please note that 32 bits
39	 * can *always* be represented with at most 10 decimal digits,
40	 * including a possible rounding from the fractional part.
41	 */
42	cp = cpend = cpdec = &cbuf[10];
43	for (dec = (int)(cp - cbuf); dec > 0 && fpi != 0; dec--) {
44		/* can add another digit */
45		u_int32 digit;
46
47		digit  = fpi;
48		fpi   /= 10U;
49		digit -= (fpi << 3) + (fpi << 1); /* i*10 */
50		*--cp  = (u_char)digit;
51	}
52
53	/*
54	 * Done that, now deal with the problem of the fraction.  First
55	 * determine the number of decimal places.
56	 */
57	dec = ndec;
58	if (dec < 0)
59		dec = 0;
60	if (msec) {
61		dec   += 3;
62		cpdec += 3;
63	}
64	if ((size_t)dec > sizeof(cbuf) - (cpend - cbuf))
65		dec = (int)(sizeof(cbuf) - (cpend - cbuf));
66
67	/*
68	 * If there's a fraction to deal with, do so.
69	 */
70	for (/*NOP*/;  dec > 0 && fpv != 0;  dec--)  {
71		u_int32 digit, tmph, tmpl;
72
73		/*
74		 * The scheme here is to multiply the fraction
75		 * (0.1234...) by ten.  This moves a junk of BCD into
76		 * the units part.  record that and iterate.
77		 * multiply by shift/add in two dwords.
78		 */
79		digit = 0;
80		M_LSHIFT(digit, fpv);
81		tmph = digit;
82		tmpl = fpv;
83		M_LSHIFT(digit, fpv);
84		M_LSHIFT(digit, fpv);
85		M_ADD(digit, fpv, tmph, tmpl);
86		*cpend++ = (u_char)digit;
87	}
88
89	/* decide whether to round or simply extend by zeros */
90	if (dec > 0) {
91		/* only '0' digits left -- just reposition end */
92		cpend += dec;
93	} else {
94		/* some bits remain in 'fpv'; do round */
95		u_char *tp    = cpend;
96		int     carry = ((fpv & 0x80000000) != 0);
97
98		for (dec = (int)(tp - cbuf);  carry && dec > 0;  dec--) {
99			*--tp += 1;
100			if (*tp == 10)
101				*tp = 0;
102			else
103				carry = FALSE;
104		}
105
106		if (tp < cp) /* rounding from 999 to 1000 or similiar? */
107			cp = tp;
108	}
109
110	/*
111	 * We've now got the fraction in cbuf[], with cp pointing at
112	 * the first character, cpend pointing past the last, and
113	 * cpdec pointing at the first character past the decimal.
114	 * Remove leading zeros, then format the number into the
115	 * buffer.
116	 */
117	while (cp < cpdec && *cp == 0)
118		cp++;
119	if (cp >= cpdec)
120		cp = cpdec - 1;
121
122	bp = buf;
123	if (sign)
124		*bp++ = sign;
125	while (cp < cpend) {
126		if (cp == cpdec)
127			*bp++ = '.';
128		*bp++ = (char)(*cp++) + '0';
129	}
130	*bp = '\0';
131
132	/*
133	 * Done!
134	 */
135	return buf;
136}
137
138
139char *
140mfptoa(
141	u_int32	fpi,
142	u_int32	fpf,
143	short	ndec
144	)
145{
146	int	isneg;
147
148	isneg = M_ISNEG(fpi);
149	if (isneg) {
150		M_NEG(fpi, fpf);
151	}
152
153	return dolfptoa(fpi, fpf, (isneg?'-':'+'), ndec, FALSE);
154}
155
156
157char *
158mfptoms(
159	u_int32	fpi,
160	u_int32	fpf,
161	short	ndec
162	)
163{
164	int	isneg;
165
166	isneg = M_ISNEG(fpi);
167	if (isneg) {
168		M_NEG(fpi, fpf);
169	}
170
171	return dolfptoa(fpi, fpf, (isneg?'-':'+'), ndec, TRUE);
172}
173
174
175