1 /*        $NetBSD: dolfptoa.c,v 1.6 2024/08/18 20:47:13 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 "ntp_stdlib.h"
11 
12 char *
dolfptoa(u_int32 fpi,u_int32 fpv,char sign,short ndec,int msec)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 *
mfptoa(u_int32 fpi,u_int32 fpf,short ndec)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 *
mfptoms(u_int32 fpi,u_int32 fpf,short ndec)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