]> git.lizzy.rs Git - plan9front.git/blob - sys/src/ape/lib/fmt/fltfmt.c
sdiahci, sdodin: avoid calling kproc() while holding ilock()
[plan9front.git] / sys / src / ape / lib / fmt / fltfmt.c
1 /*
2  * The authors of this software are Rob Pike and Ken Thompson.
3  *              Copyright (c) 2002 by Lucent Technologies.
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose without fee is hereby granted, provided that this entire notice
6  * is included in all copies of any software which is or includes a copy
7  * or modification of this software and in all copies of the supporting
8  * documentation for such software.
9  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
10  * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
11  * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
12  * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
13  */
14 #include <stdio.h>
15 #include <math.h>
16 #include <float.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <stdarg.h>
21 #include "fmt.h"
22 #include "fmtdef.h"
23 #include "nan.h"
24
25 enum
26 {
27         FDEFLT  = 6,
28         NSIGNIF = 17
29 };
30
31 /*
32  * first few powers of 10, enough for about 1/2 of the
33  * total space for doubles.
34  */
35 static double pows10[] =
36 {
37           1e0,   1e1,   1e2,   1e3,   1e4,   1e5,   1e6,   1e7,   1e8,   1e9,  
38          1e10,  1e11,  1e12,  1e13,  1e14,  1e15,  1e16,  1e17,  1e18,  1e19,  
39          1e20,  1e21,  1e22,  1e23,  1e24,  1e25,  1e26,  1e27,  1e28,  1e29,  
40          1e30,  1e31,  1e32,  1e33,  1e34,  1e35,  1e36,  1e37,  1e38,  1e39,  
41          1e40,  1e41,  1e42,  1e43,  1e44,  1e45,  1e46,  1e47,  1e48,  1e49,  
42          1e50,  1e51,  1e52,  1e53,  1e54,  1e55,  1e56,  1e57,  1e58,  1e59,  
43          1e60,  1e61,  1e62,  1e63,  1e64,  1e65,  1e66,  1e67,  1e68,  1e69,  
44          1e70,  1e71,  1e72,  1e73,  1e74,  1e75,  1e76,  1e77,  1e78,  1e79,  
45          1e80,  1e81,  1e82,  1e83,  1e84,  1e85,  1e86,  1e87,  1e88,  1e89,  
46          1e90,  1e91,  1e92,  1e93,  1e94,  1e95,  1e96,  1e97,  1e98,  1e99,  
47         1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, 
48         1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, 
49         1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, 
50         1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 
51         1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, 
52         1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, 
53 };
54
55 static double
56 pow10(int n)
57 {
58         double d;
59         int neg;
60
61         neg = 0;
62         if(n < 0){
63                 if(n < DBL_MIN_10_EXP){
64                         return 0.;
65                 }
66                 neg = 1;
67                 n = -n;
68         }else if(n > DBL_MAX_10_EXP){
69                 return HUGE_VAL;
70         }
71         if(n < (int)(sizeof(pows10)/sizeof(pows10[0])))
72                 d = pows10[n];
73         else{
74                 d = pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
75                 for(;;){
76                         n -= sizeof(pows10)/sizeof(pows10[0]) - 1;
77                         if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))){
78                                 d *= pows10[n];
79                                 break;
80                         }
81                         d *= pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
82                 }
83         }
84         if(neg){
85                 return 1./d;
86         }
87         return d;
88 }
89
90 static int
91 xadd(char *a, int n, int v)
92 {
93         char *b;
94         int c;
95
96         if(n < 0 || n >= NSIGNIF)
97                 return 0;
98         for(b = a+n; b >= a; b--) {
99                 c = *b + v;
100                 if(c <= '9') {
101                         *b = c;
102                         return 0;
103                 }
104                 *b = '0';
105                 v = 1;
106         }
107         *a = '1';       /* overflow adding */
108         return 1;
109 }
110
111 static int
112 xsub(char *a, int n, int v)
113 {
114         char *b;
115         int c;
116
117         for(b = a+n; b >= a; b--) {
118                 c = *b - v;
119                 if(c >= '0') {
120                         *b = c;
121                         return 0;
122                 }
123                 *b = '9';
124                 v = 1;
125         }
126         *a = '9';       /* underflow subtracting */
127         return 1;
128 }
129
130 static void
131 xaddexp(char *p, int e)
132 {
133         char se[9];
134         int i;
135
136         *p++ = 'e';
137         if(e < 0) {
138                 *p++ = '-';
139                 e = -e;
140         }
141         i = 0;
142         while(e) {
143                 se[i++] = e % 10 + '0';
144                 e /= 10;
145         }
146         if(i == 0) {
147                 *p++ = '0';
148         } else {
149                 while(i > 0)
150                         *p++ = se[--i];
151         }
152         *p++ = '\0';
153 }
154
155 static char*
156 xdodtoa(char *s1, double f, int chr, int prec, int *decpt, int *rsign)
157 {
158         char s2[NSIGNIF+10];
159         double g, h;
160         int e, d, i;
161         int c2, sign, oerr;
162
163         if(chr == 'F')
164                 chr = 'f';
165         if(prec > NSIGNIF)
166                 prec = NSIGNIF;
167         if(prec < 0)
168                 prec = 0;
169         if(__isNaN(f)) {
170                 *decpt = 9999;
171                 *rsign = 0;
172                 strcpy(s1, "nan");
173                 return &s1[3];
174         }
175         sign = 0;
176         if(f < 0) {
177                 f = -f;
178                 sign++;
179         }
180         *rsign = sign;
181         if(__isInf(f, 1) || __isInf(f, -1)) {
182                 *decpt = 9999;
183                 strcpy(s1, "inf");
184                 return &s1[3];
185         }
186
187         e = 0;
188         g = f;
189         if(g != 0) {
190                 frexp(f, &e);
191                 e = (int)(e * .301029995664);
192                 if(e >= -150 && e <= +150) {
193                         d = 0;
194                         h = f;
195                 } else {
196                         d = e/2;
197                         h = f * pow10(-d);
198                 }
199                 g = h * pow10(d-e);
200                 while(g < 1) {
201                         e--;
202                         g = h * pow10(d-e);
203                 }
204                 while(g >= 10) {
205                         e++;
206                         g = h * pow10(d-e);
207                 }
208         }
209
210         /*
211          * convert NSIGNIF digits and convert
212          * back to get accuracy.
213          */
214         for(i=0; i<NSIGNIF; i++) {
215                 d = (int)g;
216                 s1[i] = d + '0';
217                 g = (g - d) * 10;
218         }
219         s1[i] = 0;
220
221         /*
222          * try decimal rounding to eliminate 9s
223          */
224         c2 = prec + 1;
225         if(chr == 'f')
226                 c2 += e;
227         oerr = errno;
228         if(c2 >= NSIGNIF-2) {
229                 strcpy(s2, s1);
230                 d = e;
231                 s1[NSIGNIF-2] = '0';
232                 s1[NSIGNIF-1] = '0';
233                 xaddexp(s1+NSIGNIF, e-NSIGNIF+1);
234                 g = fmtstrtod(s1, nil);
235                 if(g == f)
236                         goto found;
237                 if(xadd(s1, NSIGNIF-3, 1)) {
238                         e++;
239                         xaddexp(s1+NSIGNIF, e-NSIGNIF+1);
240                 }
241                 g = fmtstrtod(s1, nil);
242                 if(g == f)
243                         goto found;
244                 strcpy(s1, s2);
245                 e = d;
246         }
247
248         /*
249          * convert back so s1 gets exact answer
250          */
251         for(d = 0; d < 10; d++) {
252                 xaddexp(s1+NSIGNIF, e-NSIGNIF+1);
253                 g = fmtstrtod(s1, nil);
254                 if(f > g) {
255                         if(xadd(s1, NSIGNIF-1, 1))
256                                 e--;
257                         continue;
258                 }
259                 if(f < g) {
260                         if(xsub(s1, NSIGNIF-1, 1))
261                                 e++;
262                         continue;
263                 }
264                 break;
265         }
266
267 found:
268         errno = oerr;
269
270         /*
271          * sign
272          */
273         d = 0;
274         i = 0;
275
276         /*
277          * round & adjust 'f' digits
278          */
279         c2 = prec + 1;
280         if(chr == 'f'){
281                 if(xadd(s1, c2+e, 5))
282                         e++;
283                 c2 += e;
284                 if(c2 < 0){
285                         c2 = 0;
286                         e = -prec - 1;
287                 }
288         }else{
289                 if(xadd(s1, c2, 5))
290                         e++;
291         }
292         if(c2 > NSIGNIF){
293                 c2 = NSIGNIF;
294         }
295
296         *decpt = e + 1;
297
298         /*
299          * terminate the converted digits
300          */
301         s1[c2] = '\0';
302         return &s1[c2];
303 }
304
305 /*
306  * this function works like the standard dtoa, if you want it.
307  */
308 #if 0
309 static char*
310 __dtoa(double f, int mode, int ndigits, int *decpt, int *rsign, char **rve)
311 {
312         static char s2[NSIGNIF + 10];
313         char *es;
314         int chr, prec;
315
316         switch(mode) {
317         /* like 'e' */
318         case 2:
319         case 4:
320         case 6:
321         case 8:
322                 chr = 'e';
323                 break;
324         /* like 'g' */
325         case 0:
326         case 1:
327         default:
328                 chr = 'g';
329                 break;
330         /* like 'f' */
331         case 3:
332         case 5:
333         case 7:
334         case 9:
335                 chr = 'f';
336                 break;
337         }
338
339         if(chr != 'f' && ndigits){
340                 ndigits--;
341         }
342         prec = ndigits;
343         if(prec > NSIGNIF)
344                 prec = NSIGNIF;
345         if(ndigits == 0)
346                 prec = NSIGNIF;
347         es = xdodtoa(s2, f, chr, prec, decpt, rsign);
348
349         /*
350          * strip trailing 0
351          */
352         for(; es > s2 + 1; es--){
353                 if(es[-1] != '0'){
354                         break;
355                 }
356         }
357         *es = '\0';
358         if(rve != NULL)
359                 *rve = es;
360         return s2;
361 }
362 #endif
363
364 static int
365 fmtzdotpad(Fmt *f, int n, int pt)
366 {
367         char *t, *s;
368         int i;
369         Rune *rt, *rs;
370
371         if(f->runes){
372                 rt = (Rune*)f->to;
373                 rs = (Rune*)f->stop;
374                 for(i = 0; i < n; i++){
375                         if(i == pt){
376                                 FMTRCHAR(f, rt, rs, '.');
377                         }
378                         FMTRCHAR(f, rt, rs, '0');
379                 }
380                 f->nfmt += rt - (Rune*)f->to;
381                 f->to = rt;
382         }else{
383                 t = (char*)f->to;
384                 s = (char*)f->stop;
385                 for(i = 0; i < n; i++){
386                         if(i == pt){
387                                 FMTCHAR(f, t, s, '.');
388                         }
389                         FMTCHAR(f, t, s, '0');
390                 }
391                 f->nfmt += t - (char *)f->to;
392                 f->to = t;
393         }
394         return 0;
395 }
396
397 int
398 __efgfmt(Fmt *fmt)
399 {
400         double f;
401         char s1[NSIGNIF+10];
402         int e, d, n;
403         int c1, c2, c3, c4, ucase, sign, chr, prec, fl;
404
405         f = va_arg(fmt->args, double);
406         prec = FDEFLT;
407         fl = fmt->flags;
408         fmt->flags = 0;
409         if(fl & FmtPrec)
410                 prec = fmt->prec;
411         chr = fmt->r;
412         ucase = 0;
413         if(chr == 'E'){
414                 chr = 'e';
415                 ucase = 1;
416         }else if(chr == 'F'){
417                 chr = 'f';
418                 ucase = 1;
419         }else if(chr == 'G'){
420                 chr = 'g';
421                 ucase = 1;
422         }
423         if(prec > 0 && chr == 'g')
424                 prec--;
425         if(prec < 0)
426                 prec = 0;
427
428         xdodtoa(s1, f, chr, prec, &e, &sign);
429         e--;
430         if(*s1 == 'i' || *s1 == 'n'){
431                 if(ucase){
432                         if(*s1 == 'i'){
433                                 strcpy(s1, "INF");
434                         }else{
435                                 strcpy(s1, "NAN");
436                         }
437                 }
438                 fmt->flags = fl & (FmtWidth|FmtLeft);
439                 return __fmtcpy(fmt, (const void*)s1, 3, 3);
440         }
441
442         /*
443          * copy into final place
444          * c1 digits of leading '0'
445          * c2 digits from conversion
446          * c3 digits of trailing '0'
447          * c4 digits after '.'
448          */
449         c1 = 0;
450         c2 = prec + 1;
451         c3 = 0;
452         c4 = prec;
453         switch(chr) {
454         default:
455                 chr = 'e';
456                 break;
457         case 'g':
458                 /*
459                  * decide on 'e' of 'f' style convers
460                  */
461                 if(e >= -4 && e <= prec) {
462                         c1 = -e;
463                         c4 = prec - e;
464                         chr = 'h';      /* flag for 'f' style */
465                 }
466                 break;
467         case 'f':
468                 c1 = -e;
469                 if(c1 > prec)
470                         c1 = prec + 1;
471                 c2 += e;
472                 break;
473         }
474
475         /*
476          * clean up c1 c2 and c3
477          */
478         if(c1 < 0)
479                 c1 = 0;
480         if(c2 < 0)
481                 c2 = 0;
482         if(c2 > NSIGNIF) {
483                 c3 = c2-NSIGNIF;
484                 c2 = NSIGNIF;
485         }
486
487         /*
488          * trim trailing zeros for %g
489          */
490         if(!(fl & FmtSharp)
491         && (chr == 'g' || chr == 'h')){
492                 if(c4 >= c3){
493                         c4 -= c3;
494                         c3 = 0;
495                 }else{
496                         c3 -= c4;
497                         c4 = 0;
498                 }
499                 while(c4 && c2 > 1 && s1[c2 - 1] == '0'){
500                         c4--;
501                         c2--;
502                 }
503         }
504
505         /*
506          * calculate the total length
507          */
508         n = c1 + c2 + c3;
509         if(sign || (fl & (FmtSign|FmtSpace)))
510                 n++;
511         if(c4 || (fl & FmtSharp)){
512                 n++;
513         }
514         if(chr == 'e' || chr == 'g'){
515                 n += 4;
516                 if(e >= 100)
517                         n++;
518         }
519
520         /*
521          * pad to width if right justified
522          */
523         if((fl & (FmtWidth|FmtLeft)) == FmtWidth && n < fmt->width){
524                 if(fl & FmtZero){
525                         c1 += fmt->width - n;
526                 }else{
527                         if(__fmtpad(fmt, fmt->width - n) < 0){
528                                 return -1;
529                         }
530                 }
531         }
532
533         /*
534          * sign
535          */
536         d = 0;
537         if(sign)
538                 d = '-';
539         else if(fl & FmtSign)
540                 d = '+';
541         else if(fl & FmtSpace)
542                 d = ' ';
543         if(d && fmtrune(fmt, d) < 0){
544                 return -1;
545         }
546
547         /*
548          * copy digits
549          */
550         c4 = c1 + c2 + c3 - c4;
551         if(c1 > 0){
552                 if(fmtzdotpad(fmt, c1, c4) < 0){
553                         return -1;
554                 }
555                 c4 -= c1;
556         }
557         d = 0;
558         if(c4 >= 0 && c4 < c2){
559                 if(__fmtcpy(fmt, s1, c4, c4) < 0 || fmtrune(fmt, '.') < 0)
560                         return -1;
561                 d = c4;
562                 c2 -= c4;
563                 c4 = -1;
564         }
565         if(__fmtcpy(fmt, (const void*)(s1 + d), c2, c2) < 0){
566                 return -1;
567         }
568         c4 -= c2;
569         if(c3 > 0){
570                 if(fmtzdotpad(fmt, c3, c4) < 0){
571                         return -1;
572                 }
573                 c4 -= c3;
574         }
575
576         /*
577          * strip trailing '0' on g conv
578          */
579         if((fl & FmtSharp) && c4 == 0 && fmtrune(fmt, '.') < 0){
580                 return -1;
581         }
582         if(chr == 'e' || chr == 'g') {
583                 d = 0;
584                 if(ucase)
585                         s1[d++] = 'E';
586                 else
587                         s1[d++] = 'e';
588                 c1 = e;
589                 if(c1 < 0) {
590                         s1[d++] = '-';
591                         c1 = -c1;
592                 } else
593                         s1[d++] = '+';
594                 if(c1 >= 100) {
595                         s1[d++] = c1/100 + '0';
596                         c1 = c1%100;
597                 }
598                 s1[d++] = c1/10 + '0';
599                 s1[d++] = c1%10 + '0';
600                 if(__fmtcpy(fmt, s1, d, d) < 0){
601                         return -1;
602                 }
603         }
604         if((fl & (FmtWidth|FmtLeft)) == (FmtWidth|FmtLeft) && n < fmt->width){
605                 if(__fmtpad(fmt, fmt->width - n) < 0){
606                         return -1;
607                 }
608         }
609         return 0;
610 }