]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libc/fmt/fltfmt.c
pc: replace duplicated and broken mmu flush code in vunmap()
[plan9front.git] / sys / src / libc / fmt / fltfmt.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include "fmtdef.h"
5
6 enum
7 {
8         FDIGIT  = 30,
9         FDEFLT  = 6,
10         NSIGNIF = 17,
11         NEXP10  = 308,
12 };
13
14 static int
15 xadd(char *a, int n, int v)
16 {
17         char *b;
18         int c;
19
20         if(n < 0 || n >= NSIGNIF)
21                 return 0;
22         for(b = a+n; b >= a; b--) {
23                 c = *b + v;
24                 if(c <= '9') {
25                         *b = c;
26                         return 0;
27                 }
28                 *b = '0';
29                 v = 1;
30         }
31         *a = '1';       // overflow adding
32         return 1;
33 }
34
35 static int
36 xsub(char *a, int n, int v)
37 {
38         char *b;
39         int c;
40
41         for(b = a+n; b >= a; b--) {
42                 c = *b - v;
43                 if(c >= '0') {
44                         *b = c;
45                         return 0;
46                 }
47                 *b = '9';
48                 v = 1;
49         }
50         *a = '9';       // underflow subtracting
51         return 1;
52 }
53
54 static void
55 xdtoa(Fmt *fmt, char *s2, double f)
56 {
57         char s1[NSIGNIF+10];
58         double g, h;
59         int e, d, i, n;
60         int c1, c2, c3, c4, ucase, sign, chr, prec;
61
62         prec = FDEFLT;
63         if(fmt->flags & FmtPrec)
64                 prec = fmt->prec;
65         if(prec > FDIGIT)
66                 prec = FDIGIT;
67         if(isNaN(f)) {
68                 strcpy(s2, "NaN");
69                 return;
70         }
71         if(isInf(f, 1)) {
72                 strcpy(s2, "+Inf");
73                 return;
74         }
75         if(isInf(f, -1)) {
76                 strcpy(s2, "-Inf");
77                 return;
78         }
79         sign = 0;
80         if(f < 0) {
81                 f = -f;
82                 sign++;
83         }
84         ucase = 0;
85         chr = fmt->r;
86         if(isupper(chr)) {
87                 ucase = 1;
88                 chr = tolower(chr);
89         }
90
91         e = 0;
92         g = f;
93         if(g != 0) {
94                 frexp(f, &e);
95                 e = e * .301029995664;
96                 if(e >= -150 && e <= +150) {
97                         d = 0;
98                         h = f;
99                 } else {
100                         d = e/2;
101                         h = f * pow10(-d);
102                 }
103                 g = h * pow10(d-e);
104                 while(g < 1) {
105                         e--;
106                         g = h * pow10(d-e);
107                 }
108                 while(g >= 10) {
109                         e++;
110                         g = h * pow10(d-e);
111                 }
112         }
113
114         /*
115          * convert NSIGNIF digits and convert
116          * back to get accuracy.
117          */
118         for(i=0; i<NSIGNIF; i++) {
119                 d = g;
120                 s1[i] = d + '0';
121                 g = (g - d) * 10;
122         }
123         s1[i] = 0;
124
125         /*
126          * try decimal rounding to eliminate 9s
127          */
128         c2 = prec + 1;
129         if(chr == 'f')
130                 c2 += e;
131         if(c2 >= NSIGNIF-2) {
132                 strcpy(s2, s1);
133                 d = e;
134                 s1[NSIGNIF-2] = '0';
135                 s1[NSIGNIF-1] = '0';
136                 sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
137                 g = strtod(s1, nil);
138                 if(g == f)
139                         goto found;
140                 if(xadd(s1, NSIGNIF-3, 1)) {
141                         e++;
142                         sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
143                 }
144                 g = strtod(s1, nil);
145                 if(g == f)
146                         goto found;
147                 strcpy(s1, s2);
148                 e = d;
149         }
150
151         /*
152          * convert back so s1 gets exact answer
153          */
154         for(;;) {
155                 sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
156                 g = strtod(s1, nil);
157                 if(f > g) {
158                         if(xadd(s1, NSIGNIF-1, 1))
159                                 e--;
160                         continue;
161                 }
162                 if(f < g) {
163                         if(xsub(s1, NSIGNIF-1, 1))
164                                 e++;
165                         continue;
166                 }
167                 break;
168         }
169
170 found:
171         /*
172          * sign
173          */
174         d = 0;
175         i = 0;
176         if(sign)
177                 s2[d++] = '-';
178         else if(fmt->flags & FmtSign)
179                 s2[d++] = '+';
180         else if(fmt->flags & FmtSpace)
181                 s2[d++] = ' ';
182
183         /*
184          * copy into final place
185          * c1 digits of leading '0'
186          * c2 digits from conversion
187          * c3 digits of trailing '0'
188          * c4 digits after '.'
189          */
190         if(chr == 'g' && prec > 0) /* Significant figures. */
191                 prec--;
192         c1 = 0;
193         c2 = prec + 1;
194         c3 = 0;
195         c4 = prec;
196         switch(chr) {
197         default:
198                 if(xadd(s1, c2, 5))
199                         e++;
200                 break;
201         case 'g':
202                 /*
203                  * decide on 'e' of 'f' style convers
204                  */
205                 if(xadd(s1, c2, 5))
206                         e++;
207                 if(e >= -4 && e <= prec) {
208                         c1 = -e;
209                         c4 = prec - e;
210                         chr = 'h';      // flag for 'f' style
211                 }
212                 break;
213         case 'f':
214                 if(xadd(s1, c2+e, 5))
215                         e++;
216                 c1 = -e;
217                 if(c1 > prec)
218                         c1 = c2;
219                 c2 += e;
220                 break;
221         }
222
223         /*
224          * clean up c1 c2 and c3
225          */
226         if(c1 < 0)
227                 c1 = 0;
228         if(c2 < 0)
229                 c2 = 0;
230         if(c2 > NSIGNIF) {
231                 c3 = c2-NSIGNIF;
232                 c2 = NSIGNIF;
233         }
234
235         /*
236          * copy digits
237          */
238         while(c1 > 0) {
239                 if(c1+c2+c3 == c4)
240                         s2[d++] = '.';
241                 s2[d++] = '0';
242                 c1--;
243         }
244         while(c2 > 0) {
245                 if(c2+c3 == c4)
246                         s2[d++] = '.';
247                 s2[d++] = s1[i++];
248                 c2--;
249         }
250         while(c3 > 0) {
251                 if(c3 == c4)
252                         s2[d++] = '.';
253                 s2[d++] = '0';
254                 c3--;
255         }
256
257         /*
258          * strip trailing '0' on g conv
259          */
260         if(fmt->flags & FmtSharp) {
261                 if(0 == c4)
262                         s2[d++] = '.';
263         } else
264         if(chr == 'g' || chr == 'h') {
265                 for(n=d-1; n>=0; n--)
266                         if(s2[n] != '0')
267                                 break;
268                 for(i=n; i>=0; i--)
269                         if(s2[i] == '.') {
270                                 d = n;
271                                 if(i != n)
272                                         d++;
273                                 break;
274                         }
275         }
276         if(chr == 'e' || chr == 'g') {
277                 if(ucase)
278                         s2[d++] = 'E';
279                 else
280                         s2[d++] = 'e';
281                 c1 = e;
282                 if(c1 < 0) {
283                         s2[d++] = '-';
284                         c1 = -c1;
285                 } else
286                         s2[d++] = '+';
287                 if(c1 >= 100) {
288                         s2[d++] = c1/100 + '0';
289                         c1 = c1%100;
290                 }
291                 s2[d++] = c1/10 + '0';
292                 s2[d++] = c1%10 + '0';
293         }
294         s2[d] = 0;
295 }
296
297 int
298 _floatfmt(Fmt *fmt, double f)
299 {
300         char s[1+NEXP10+1+FDIGIT+1];
301
302         /*
303          * The max length of a %f string is
304          *      '[+-]'+"max exponent"+'.'+"max precision"+'\0'
305          * which is 341 currently.
306          */
307         xdtoa(fmt, s, f);
308         fmt->flags &= FmtWidth|FmtLeft;
309         _fmtcpy(fmt, s, strlen(s), strlen(s));
310         return 0;
311 }
312
313 int
314 _efgfmt(Fmt *f)
315 {
316         double d;
317
318         d = va_arg(f->args, double);
319         return _floatfmt(f, d);
320 }