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