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