xref: /freebsd/contrib/ntp/libntp/dolfptoa.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
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 
12 char *
13 dolfptoa(
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 
139 char *
140 mfptoa(
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 
157 char *
158 mfptoms(
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