xref: /freebsd/contrib/ntp/libntp/dolfptoa.c (revision b1f9167f94059fd55c630891d359bcff987bd7eb)
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 	 * safeguard against sign extensions and other mishaps on 64 bit platforms
40 	 * the code following is designed for and only for 32-bit inputs and
41 	 * only 32-bit worth of input are supplied.
42          */
43 	fpi &= 0xffffffff;
44 	fpv &= 0xffffffff;
45 
46 	/*
47 	 * Work on the integral part.  This is biased by what I know
48 	 * compiles fairly well for a 68000.
49 	 */
50 	cp = cpend = &cbuf[10];
51 	lwork = fpi;
52 	if (lwork & 0xffff0000) {
53 		register u_long lten = 10;
54 		register u_long ltmp;
55 
56 		do {
57 			ltmp = lwork;
58 			lwork /= lten;
59 			ltmp -= (lwork << 3) + (lwork << 1);
60 			if (cp < cbuf) abort(); /* rather die a horrible death than trash the memory */
61 			*--cp = (u_char)ltmp;
62 		} while (lwork & 0xffff0000);
63 	}
64 	if (lwork != 0) {
65 		register u_short sten = 10;
66 		register u_short stmp;
67 		register u_short swork = (u_short)lwork;
68 
69 		do {
70 			stmp = swork;
71 			swork = (u_short) (swork/sten);
72 			stmp = (u_short)(stmp - ((swork<<3) + (swork<<1)));
73 		        if (cp < cbuf) abort(); /* rather die a horrible death than trash the memory */
74 			*--cp = (u_char)stmp;
75 		} while (swork != 0);
76 	}
77 
78 	/*
79 	 * Done that, now deal with the problem of the fraction.  First
80 	 * determine the number of decimal places.
81 	 */
82 	if (msec) {
83 		dec = ndec + 3;
84 		if (dec < 3)
85 		    dec = 3;
86 		cpdec = &cbuf[13];
87 	} else {
88 		dec = ndec;
89 		if (dec < 0)
90 		    dec = 0;
91 		cpdec = &cbuf[10];
92 	}
93 	if (dec > 12)
94 	    dec = 12;
95 
96 	/*
97 	 * If there's a fraction to deal with, do so.
98 	 */
99 	if (fpv != 0) {
100 		l_fp work;
101 
102 		work.l_ui = 0;
103 		work.l_uf = fpv;
104 		while (dec > 0) {
105 			l_fp ftmp;
106 
107 			dec--;
108 			/*
109 			 * The scheme here is to multiply the
110 			 * fraction (0.1234...) by ten.  This moves
111 			 * a junk of BCD into the units part.
112 			 * record that and iterate.
113 			 */
114 			work.l_ui = 0;
115 			L_LSHIFT(&work);
116 			ftmp = work;
117 			L_LSHIFT(&work);
118 			L_LSHIFT(&work);
119 			L_ADD(&work, &ftmp);
120 			*cpend++ = (u_char)work.l_ui;
121 			if (work.l_uf == 0)
122 			    break;
123 			if (cpend > (cbuf + sizeof(cbuf))) abort(); /* rather die a horrible death than trash the memory */
124 		}
125 
126 		/*
127 		 * Rounding is rotten
128 		 */
129 		if (work.l_uf & 0x80000000) {
130 			register u_char *tp = cpend;
131 
132 			*(--tp) += 1;
133 			while (*tp >= 10) {
134 				*tp = 0;
135 				*(--tp) += 1;
136 			};
137 			if (tp < cp)
138 			    cp = tp;
139 		}
140 	}
141 	cpend += dec;
142 
143 
144 	/*
145 	 * We've now got the fraction in cbuf[], with cp pointing at
146 	 * the first character, cpend pointing past the last, and
147 	 * cpdec pointing at the first character past the decimal.
148 	 * Remove leading zeros, then format the number into the
149 	 * buffer.
150 	 */
151 	while (cp < cpdec) {
152 		if (*cp != 0)
153 		    break;
154 		cp++;
155 	}
156 	if (cp == cpdec)
157 	    --cp;
158 
159 	bp = buf;
160 	if (neg)
161 	    *bp++ = '-';
162 	while (cp < cpend) {
163 		if (cp == cpdec)
164 		    *bp++ = '.';
165 		*bp++ = (char)(*cp++ + '0');	/* ascii dependent? */
166 	}
167 	*bp = '\0';
168 
169 	/*
170 	 * Done!
171 	 */
172 	return buf;
173 }
174