xref: /freebsd/contrib/ntp/libntp/dolfptoa.c (revision 9336e0699bda8a301cd2bfa37106b6ec5e32012e)
1 /*
2  * dolfptoa - do the grunge work of converting an l_fp number to decimal
3  */
4 #include <stdio.h>
5 
6 #include "ntp_fp.h"
7 #include "lib_strbuf.h"
8 #include "ntp_string.h"
9 #include "ntp_stdlib.h"
10 
11 char *
12 dolfptoa(
13 	u_long fpi,
14 	u_long fpv,
15 	int neg,
16 	short ndec,
17 	int msec
18 	)
19 {
20 	register u_char *cp, *cpend;
21 	register u_long lwork;
22 	register int dec;
23 	u_char cbuf[24];
24 	u_char *cpdec;
25 	char *buf;
26 	char *bp;
27 
28 	/*
29 	 * Get a string buffer before starting
30 	 */
31 	LIB_GETBUF(buf);
32 
33 	/*
34 	 * Zero the character buffer
35 	 */
36 	memset((char *) cbuf, 0, sizeof(cbuf));
37 
38 	/*
39 	 * Work on the integral part.  This is biased by what I know
40 	 * compiles fairly well for a 68000.
41 	 */
42 	cp = cpend = &cbuf[10];
43 	lwork = fpi;
44 	if (lwork & 0xffff0000) {
45 		register u_long lten = 10;
46 		register u_long ltmp;
47 
48 		do {
49 			ltmp = lwork;
50 			lwork /= lten;
51 			ltmp -= (lwork << 3) + (lwork << 1);
52 			*--cp = (u_char)ltmp;
53 		} while (lwork & 0xffff0000);
54 	}
55 	if (lwork != 0) {
56 		register u_short sten = 10;
57 		register u_short stmp;
58 		register u_short swork = (u_short)lwork;
59 
60 		do {
61 			stmp = swork;
62 			swork = (u_short) (swork/sten);
63 			stmp = (u_short)(stmp - ((swork<<3) + (swork<<1)));
64 			*--cp = (u_char)stmp;
65 		} while (swork != 0);
66 	}
67 
68 	/*
69 	 * Done that, now deal with the problem of the fraction.  First
70 	 * determine the number of decimal places.
71 	 */
72 	if (msec) {
73 		dec = ndec + 3;
74 		if (dec < 3)
75 		    dec = 3;
76 		cpdec = &cbuf[13];
77 	} else {
78 		dec = ndec;
79 		if (dec < 0)
80 		    dec = 0;
81 		cpdec = &cbuf[10];
82 	}
83 	if (dec > 12)
84 	    dec = 12;
85 
86 	/*
87 	 * If there's a fraction to deal with, do so.
88 	 */
89 	if (fpv != 0) {
90 		l_fp work;
91 
92 		work.l_ui = 0;
93 		work.l_uf = fpv;
94 		while (dec > 0) {
95 			l_fp ftmp;
96 
97 			dec--;
98 			/*
99 			 * The scheme here is to multiply the
100 			 * fraction (0.1234...) by ten.  This moves
101 			 * a junk of BCD into the units part.
102 			 * record that and iterate.
103 			 */
104 			work.l_ui = 0;
105 			L_LSHIFT(&work);
106 			ftmp = work;
107 			L_LSHIFT(&work);
108 			L_LSHIFT(&work);
109 			L_ADD(&work, &ftmp);
110 			*cpend++ = (u_char)work.l_ui;
111 			if (work.l_uf == 0)
112 			    break;
113 		}
114 
115 		/*
116 		 * Rounding is rotten
117 		 */
118 		if (work.l_uf & 0x80000000) {
119 			register u_char *tp = cpend;
120 
121 			*(--tp) += 1;
122 			while (*tp >= 10) {
123 				*tp = 0;
124 				*(--tp) += 1;
125 			};
126 			if (tp < cp)
127 			    cp = tp;
128 		}
129 	}
130 	cpend += dec;
131 
132 
133 	/*
134 	 * We've now got the fraction in cbuf[], with cp pointing at
135 	 * the first character, cpend pointing past the last, and
136 	 * cpdec pointing at the first character past the decimal.
137 	 * Remove leading zeros, then format the number into the
138 	 * buffer.
139 	 */
140 	while (cp < cpdec) {
141 		if (*cp != 0)
142 		    break;
143 		cp++;
144 	}
145 	if (cp == cpdec)
146 	    --cp;
147 
148 	bp = buf;
149 	if (neg)
150 	    *bp++ = '-';
151 	while (cp < cpend) {
152 		if (cp == cpdec)
153 		    *bp++ = '.';
154 		*bp++ = (char)(*cp++ + '0');	/* ascii dependent? */
155 	}
156 	*bp = '\0';
157 
158 	/*
159 	 * Done!
160 	 */
161 	return buf;
162 }
163