fltfmt.c - 9base - revived minimalist port of Plan 9 userland to Unix | |
git clone git://git.suckless.org/9base | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
fltfmt.c (12850B) | |
--- | |
1 /* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ | |
2 #include <stdio.h> | |
3 #include <math.h> | |
4 #include <float.h> | |
5 #include <string.h> | |
6 #include <stdlib.h> | |
7 #include <errno.h> | |
8 #include <stdarg.h> | |
9 #include <fmt.h> | |
10 #include <assert.h> | |
11 #include "plan9.h" | |
12 #include "fmt.h" | |
13 #include "fmtdef.h" | |
14 #include "nan.h" | |
15 | |
16 enum | |
17 { | |
18 FDIGIT = 30, | |
19 FDEFLT = 6, | |
20 NSIGNIF = 17 | |
21 }; | |
22 | |
23 /* | |
24 * first few powers of 10, enough for about 1/2 of the | |
25 * total space for doubles. | |
26 */ | |
27 static double pows10[] = | |
28 { | |
29 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, … | |
30 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, … | |
31 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, … | |
32 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, … | |
33 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, … | |
34 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, … | |
35 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, … | |
36 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, … | |
37 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, … | |
38 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, … | |
39 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1… | |
40 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1… | |
41 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1… | |
42 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1… | |
43 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1… | |
44 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1… | |
45 }; | |
46 #define npows10 ((int)(sizeof(pows10)/sizeof(pows10[0]))) | |
47 #define pow10(x) fmtpow10(x) | |
48 | |
49 static double | |
50 pow10(int n) | |
51 { | |
52 double d; | |
53 int neg; | |
54 | |
55 neg = 0; | |
56 if(n < 0){ | |
57 neg = 1; | |
58 n = -n; | |
59 } | |
60 | |
61 if(n < npows10) | |
62 d = pows10[n]; | |
63 else{ | |
64 d = pows10[npows10-1]; | |
65 for(;;){ | |
66 n -= npows10 - 1; | |
67 if(n < npows10){ | |
68 d *= pows10[n]; | |
69 break; | |
70 } | |
71 d *= pows10[npows10 - 1]; | |
72 } | |
73 } | |
74 if(neg) | |
75 return 1./d; | |
76 return d; | |
77 } | |
78 | |
79 /* | |
80 * add 1 to the decimal integer string a of length n. | |
81 * if 99999 overflows into 10000, return 1 to tell caller | |
82 * to move the virtual decimal point. | |
83 */ | |
84 static int | |
85 xadd1(char *a, int n) | |
86 { | |
87 char *b; | |
88 int c; | |
89 | |
90 if(n < 0 || n > NSIGNIF) | |
91 return 0; | |
92 for(b = a+n-1; b >= a; b--) { | |
93 c = *b + 1; | |
94 if(c <= '9') { | |
95 *b = c; | |
96 return 0; | |
97 } | |
98 *b = '0'; | |
99 } | |
100 /* | |
101 * need to overflow adding digit. | |
102 * shift number down and insert 1 at beginning. | |
103 * decimal is known to be 0s or we wouldn't | |
104 * have gotten this far. (e.g., 99999+1 => 00000) | |
105 */ | |
106 a[0] = '1'; | |
107 return 1; | |
108 } | |
109 | |
110 /* | |
111 * subtract 1 from the decimal integer string a. | |
112 * if 10000 underflows into 09999, make it 99999 | |
113 * and return 1 to tell caller to move the virtual | |
114 * decimal point. this way, xsub1 is inverse of xadd1. | |
115 */ | |
116 static int | |
117 xsub1(char *a, int n) | |
118 { | |
119 char *b; | |
120 int c; | |
121 | |
122 if(n < 0 || n > NSIGNIF) | |
123 return 0; | |
124 for(b = a+n-1; b >= a; b--) { | |
125 c = *b - 1; | |
126 if(c >= '0') { | |
127 if(c == '0' && b == a) { | |
128 /* | |
129 * just zeroed the top digit; shift ever… | |
130 * decimal is known to be 9s or we would… | |
131 * have gotten this far. (e.g., 10000-1… | |
132 */ | |
133 *b = '9'; | |
134 return 1; | |
135 } | |
136 *b = c; | |
137 return 0; | |
138 } | |
139 *b = '9'; | |
140 } | |
141 /* | |
142 * can't get here. the number a is always normalized | |
143 * so that it has a nonzero first digit. | |
144 */ | |
145 abort(); | |
146 } | |
147 | |
148 /* | |
149 * format exponent like sprintf(p, "e%+02d", e) | |
150 */ | |
151 static void | |
152 xfmtexp(char *p, int e, int ucase) | |
153 { | |
154 char se[9]; | |
155 int i; | |
156 | |
157 *p++ = ucase ? 'E' : 'e'; | |
158 if(e < 0) { | |
159 *p++ = '-'; | |
160 e = -e; | |
161 } else | |
162 *p++ = '+'; | |
163 i = 0; | |
164 while(e) { | |
165 se[i++] = e % 10 + '0'; | |
166 e /= 10; | |
167 } | |
168 while(i < 2) | |
169 se[i++] = '0'; | |
170 while(i > 0) | |
171 *p++ = se[--i]; | |
172 *p++ = '\0'; | |
173 } | |
174 | |
175 /* | |
176 * compute decimal integer m, exp such that: | |
177 * f = m*10^exp | |
178 * m is as short as possible with losing exactness | |
179 * assumes special cases (NaN, +Inf, -Inf) have been handled. | |
180 */ | |
181 static void | |
182 xdtoa(double f, char *s, int *exp, int *neg, int *ns) | |
183 { | |
184 int c, d, e2, e, ee, i, ndigit, oerrno; | |
185 char tmp[NSIGNIF+10]; | |
186 double g; | |
187 | |
188 oerrno = errno; /* in case strtod smashes errno */ | |
189 | |
190 /* | |
191 * make f non-negative. | |
192 */ | |
193 *neg = 0; | |
194 if(f < 0) { | |
195 f = -f; | |
196 *neg = 1; | |
197 } | |
198 | |
199 /* | |
200 * must handle zero specially. | |
201 */ | |
202 if(f == 0){ | |
203 *exp = 0; | |
204 s[0] = '0'; | |
205 s[1] = '\0'; | |
206 *ns = 1; | |
207 return; | |
208 } | |
209 | |
210 /* | |
211 * find g,e such that f = g*10^e. | |
212 * guess 10-exponent using 2-exponent, then fine tune. | |
213 */ | |
214 frexp(f, &e2); | |
215 e = (int)(e2 * .301029995664); | |
216 g = f * pow10(-e); | |
217 while(g < 1) { | |
218 e--; | |
219 g = f * pow10(-e); | |
220 } | |
221 while(g >= 10) { | |
222 e++; | |
223 g = f * pow10(-e); | |
224 } | |
225 | |
226 /* | |
227 * convert NSIGNIF digits as a first approximation. | |
228 */ | |
229 for(i=0; i<NSIGNIF; i++) { | |
230 d = (int)g; | |
231 s[i] = d+'0'; | |
232 g = (g-d) * 10; | |
233 } | |
234 s[i] = 0; | |
235 | |
236 /* | |
237 * adjust e because s is 314159... not 3.14159... | |
238 */ | |
239 e -= NSIGNIF-1; | |
240 xfmtexp(s+NSIGNIF, e, 0); | |
241 | |
242 /* | |
243 * adjust conversion until strtod(s) == f exactly. | |
244 */ | |
245 for(i=0; i<10; i++) { | |
246 g = fmtstrtod(s, nil); | |
247 if(f > g) { | |
248 if(xadd1(s, NSIGNIF)) { | |
249 /* gained a digit */ | |
250 e--; | |
251 xfmtexp(s+NSIGNIF, e, 0); | |
252 } | |
253 continue; | |
254 } | |
255 if(f < g) { | |
256 if(xsub1(s, NSIGNIF)) { | |
257 /* lost a digit */ | |
258 e++; | |
259 xfmtexp(s+NSIGNIF, e, 0); | |
260 } | |
261 continue; | |
262 } | |
263 break; | |
264 } | |
265 | |
266 /* | |
267 * play with the decimal to try to simplify. | |
268 */ | |
269 | |
270 /* | |
271 * bump last few digits up to 9 if we can | |
272 */ | |
273 for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) { | |
274 c = s[i]; | |
275 if(c != '9') { | |
276 s[i] = '9'; | |
277 g = fmtstrtod(s, nil); | |
278 if(g != f) { | |
279 s[i] = c; | |
280 break; | |
281 } | |
282 } | |
283 } | |
284 | |
285 /* | |
286 * add 1 in hopes of turning 9s to 0s | |
287 */ | |
288 if(s[NSIGNIF-1] == '9') { | |
289 strcpy(tmp, s); | |
290 ee = e; | |
291 if(xadd1(tmp, NSIGNIF)) { | |
292 ee--; | |
293 xfmtexp(tmp+NSIGNIF, ee, 0); | |
294 } | |
295 g = fmtstrtod(tmp, nil); | |
296 if(g == f) { | |
297 strcpy(s, tmp); | |
298 e = ee; | |
299 } | |
300 } | |
301 | |
302 /* | |
303 * bump last few digits down to 0 as we can. | |
304 */ | |
305 for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) { | |
306 c = s[i]; | |
307 if(c != '0') { | |
308 s[i] = '0'; | |
309 g = fmtstrtod(s, nil); | |
310 if(g != f) { | |
311 s[i] = c; | |
312 break; | |
313 } | |
314 } | |
315 } | |
316 | |
317 /* | |
318 * remove trailing zeros. | |
319 */ | |
320 ndigit = NSIGNIF; | |
321 while(ndigit > 1 && s[ndigit-1] == '0'){ | |
322 e++; | |
323 --ndigit; | |
324 } | |
325 s[ndigit] = 0; | |
326 *exp = e; | |
327 *ns = ndigit; | |
328 errno = oerrno; | |
329 } | |
330 | |
331 #ifdef PLAN9PORT | |
332 static char *special[] = { "NaN", "NaN", "+Inf", "+Inf", "-Inf", "-Inf" … | |
333 #else | |
334 static char *special[] = { "nan", "NAN", "inf", "INF", "-inf", "-INF" }; | |
335 #endif | |
336 | |
337 int | |
338 __efgfmt(Fmt *fmt) | |
339 { | |
340 char buf[NSIGNIF+10], *dot, *digits, *p, *s, suf[10], *t; | |
341 double f; | |
342 int c, chr, dotwid, e, exp, fl, ndigits, neg, newndigits; | |
343 int pad, point, prec, realchr, sign, sufwid, ucase, wid, z1, z2; | |
344 Rune r, *rs, *rt; | |
345 | |
346 if(fmt->flags&FmtLong) | |
347 f = va_arg(fmt->args, long double); | |
348 else | |
349 f = va_arg(fmt->args, double); | |
350 | |
351 /* | |
352 * extract formatting flags | |
353 */ | |
354 fl = fmt->flags; | |
355 fmt->flags = 0; | |
356 prec = FDEFLT; | |
357 if(fl & FmtPrec) | |
358 prec = fmt->prec; | |
359 chr = fmt->r; | |
360 ucase = 0; | |
361 switch(chr) { | |
362 case 'A': | |
363 case 'E': | |
364 case 'F': | |
365 case 'G': | |
366 chr += 'a'-'A'; | |
367 ucase = 1; | |
368 break; | |
369 } | |
370 | |
371 /* | |
372 * pick off special numbers. | |
373 */ | |
374 if(__isNaN(f)) { | |
375 s = special[0+ucase]; | |
376 special: | |
377 fmt->flags = fl & (FmtWidth|FmtLeft); | |
378 return __fmtcpy(fmt, s, strlen(s), strlen(s)); | |
379 } | |
380 if(__isInf(f, 1)) { | |
381 s = special[2+ucase]; | |
382 goto special; | |
383 } | |
384 if(__isInf(f, -1)) { | |
385 s = special[4+ucase]; | |
386 goto special; | |
387 } | |
388 | |
389 /* | |
390 * get exact representation. | |
391 */ | |
392 digits = buf; | |
393 xdtoa(f, digits, &exp, &neg, &ndigits); | |
394 | |
395 /* | |
396 * get locale's decimal point. | |
397 */ | |
398 dot = fmt->decimal; | |
399 if(dot == nil) | |
400 dot = "."; | |
401 dotwid = utflen(dot); | |
402 | |
403 /* | |
404 * now the formatting fun begins. | |
405 * compute parameters for actual fmt: | |
406 * | |
407 * pad: number of spaces to insert before/after field. | |
408 * z1: number of zeros to insert before digits | |
409 * z2: number of zeros to insert after digits | |
410 * point: number of digits to print before decimal point | |
411 * ndigits: number of digits to use from digits[] | |
412 * suf: trailing suffix, like "e-5" | |
413 */ | |
414 realchr = chr; | |
415 switch(chr){ | |
416 case 'g': | |
417 /* | |
418 * convert to at most prec significant digits. (prec=0 m… | |
419 */ | |
420 if(prec == 0) | |
421 prec = 1; | |
422 if(ndigits > prec) { | |
423 if(digits[prec] >= '5' && xadd1(digits, prec)) | |
424 exp++; | |
425 exp += ndigits-prec; | |
426 ndigits = prec; | |
427 } | |
428 | |
429 /* | |
430 * extra rules for %g (implemented below): | |
431 * trailing zeros removed after decimal unless Fm… | |
432 * decimal point only if digit follows. | |
433 */ | |
434 | |
435 /* fall through to %e */ | |
436 default: | |
437 case 'e': | |
438 /* | |
439 * one significant digit before decimal, no leading zero… | |
440 */ | |
441 point = 1; | |
442 z1 = 0; | |
443 | |
444 /* | |
445 * decimal point is after ndigits digits right now. | |
446 * slide to be after first. | |
447 */ | |
448 e = exp + (ndigits-1); | |
449 | |
450 /* | |
451 * if this is %g, check exponent and convert prec | |
452 */ | |
453 if(realchr == 'g') { | |
454 if(-4 <= e && e < prec) | |
455 goto casef; | |
456 prec--; /* one digit before decimal; rest… | |
457 } | |
458 | |
459 /* | |
460 * compute trailing zero padding or truncate digits. | |
461 */ | |
462 if(1+prec >= ndigits) | |
463 z2 = 1+prec - ndigits; | |
464 else { | |
465 /* | |
466 * truncate digits | |
467 */ | |
468 assert(realchr != 'g'); | |
469 newndigits = 1+prec; | |
470 if(digits[newndigits] >= '5' && xadd1(digits, ne… | |
471 /* | |
472 * had 999e4, now have 100e5 | |
473 */ | |
474 e++; | |
475 } | |
476 ndigits = newndigits; | |
477 z2 = 0; | |
478 } | |
479 xfmtexp(suf, e, ucase); | |
480 sufwid = strlen(suf); | |
481 break; | |
482 | |
483 casef: | |
484 case 'f': | |
485 /* | |
486 * determine where digits go with respect to decimal poi… | |
487 */ | |
488 if(ndigits+exp > 0) { | |
489 point = ndigits+exp; | |
490 z1 = 0; | |
491 } else { | |
492 point = 1; | |
493 z1 = 1 + -(ndigits+exp); | |
494 } | |
495 | |
496 /* | |
497 * %g specifies prec = number of significant digits | |
498 * convert to number of digits after decimal point | |
499 */ | |
500 if(realchr == 'g') | |
501 prec += z1 - point; | |
502 | |
503 /* | |
504 * compute trailing zero padding or truncate digits. | |
505 */ | |
506 if(point+prec >= z1+ndigits) | |
507 z2 = point+prec - (z1+ndigits); | |
508 else { | |
509 /* | |
510 * truncate digits | |
511 */ | |
512 assert(realchr != 'g'); | |
513 newndigits = point+prec - z1; | |
514 if(newndigits < 0) { | |
515 z1 += newndigits; | |
516 newndigits = 0; | |
517 } else if(newndigits == 0) { | |
518 /* perhaps round up */ | |
519 if(digits[0] >= '5'){ | |
520 digits[0] = '1'; | |
521 newndigits = 1; | |
522 goto newdigit; | |
523 } | |
524 } else if(digits[newndigits] >= '5' && xadd1(dig… | |
525 /* | |
526 * digits was 999, is now 100; make it 1… | |
527 */ | |
528 digits[newndigits++] = '0'; | |
529 newdigit: | |
530 /* | |
531 * account for new digit | |
532 */ | |
533 if(z1) /* 0.099 => 0.100 or 0.99 … | |
534 z1--; | |
535 else /* 9.99 => 10.00 */ | |
536 point++; | |
537 } | |
538 z2 = 0; | |
539 ndigits = newndigits; | |
540 } | |
541 sufwid = 0; | |
542 break; | |
543 } | |
544 | |
545 /* | |
546 * if %g is given without FmtSharp, remove trailing zeros. | |
547 * must do after truncation, so that e.g. print %.3g 1.001 | |
548 * produces 1, not 1.00. sorry, but them's the rules. | |
549 */ | |
550 if(realchr == 'g' && !(fl & FmtSharp)) { | |
551 if(z1+ndigits+z2 >= point) { | |
552 if(z1+ndigits < point) | |
553 z2 = point - (z1+ndigits); | |
554 else{ | |
555 z2 = 0; | |
556 while(z1+ndigits > point && digits[ndigi… | |
557 ndigits--; | |
558 } | |
559 } | |
560 } | |
561 | |
562 /* | |
563 * compute width of all digits and decimal point and suffix if a… | |
564 */ | |
565 wid = z1+ndigits+z2; | |
566 if(wid > point) | |
567 wid += dotwid; | |
568 else if(wid == point){ | |
569 if(fl & FmtSharp) | |
570 wid += dotwid; | |
571 else | |
572 point++; /* do not print any decimal poin… | |
573 } | |
574 wid += sufwid; | |
575 | |
576 /* | |
577 * determine sign | |
578 */ | |
579 sign = 0; | |
580 if(neg) | |
581 sign = '-'; | |
582 else if(fl & FmtSign) | |
583 sign = '+'; | |
584 else if(fl & FmtSpace) | |
585 sign = ' '; | |
586 if(sign) | |
587 wid++; | |
588 | |
589 /* | |
590 * compute padding | |
591 */ | |
592 pad = 0; | |
593 if((fl & FmtWidth) && fmt->width > wid) | |
594 pad = fmt->width - wid; | |
595 if(pad && !(fl & FmtLeft) && (fl & FmtZero)){ | |
596 z1 += pad; | |
597 point += pad; | |
598 pad = 0; | |
599 } | |
600 | |
601 /* | |
602 * format the actual field. too bad about doing this twice. | |
603 */ | |
604 if(fmt->runes){ | |
605 if(pad && !(fl & FmtLeft) && __rfmtpad(fmt, pad) < 0) | |
606 return -1; | |
607 rt = (Rune*)fmt->to; | |
608 rs = (Rune*)fmt->stop; | |
609 if(sign) | |
610 FMTRCHAR(fmt, rt, rs, sign); | |
611 while(z1>0 || ndigits>0 || z2>0) { | |
612 if(z1 > 0){ | |
613 z1--; | |
614 c = '0'; | |
615 }else if(ndigits > 0){ | |
616 ndigits--; | |
617 c = *digits++; | |
618 }else{ | |
619 z2--; | |
620 c = '0'; | |
621 } | |
622 FMTRCHAR(fmt, rt, rs, c); | |
623 if(--point == 0) { | |
624 for(p = dot; *p; ){ | |
625 p += chartorune(&r, p); | |
626 FMTRCHAR(fmt, rt, rs, r); | |
627 } | |
628 } | |
629 } | |
630 fmt->nfmt += rt - (Rune*)fmt->to; | |
631 fmt->to = rt; | |
632 if(sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0) | |
633 return -1; | |
634 if(pad && (fl & FmtLeft) && __rfmtpad(fmt, pad) < 0) | |
635 return -1; | |
636 }else{ | |
637 if(pad && !(fl & FmtLeft) && __fmtpad(fmt, pad) < 0) | |
638 return -1; | |
639 t = (char*)fmt->to; | |
640 s = (char*)fmt->stop; | |
641 if(sign) | |
642 FMTCHAR(fmt, t, s, sign); | |
643 while(z1>0 || ndigits>0 || z2>0) { | |
644 if(z1 > 0){ | |
645 z1--; | |
646 c = '0'; | |
647 }else if(ndigits > 0){ | |
648 ndigits--; | |
649 c = *digits++; | |
650 }else{ | |
651 z2--; | |
652 c = '0'; | |
653 } | |
654 FMTCHAR(fmt, t, s, c); | |
655 if(--point == 0) | |
656 for(p=dot; *p; p++) | |
657 FMTCHAR(fmt, t, s, *p); | |
658 } | |
659 fmt->nfmt += t - (char*)fmt->to; | |
660 fmt->to = t; | |
661 if(sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0) | |
662 return -1; | |
663 if(pad && (fl & FmtLeft) && __fmtpad(fmt, pad) < 0) | |
664 return -1; | |
665 } | |
666 return 0; | |
667 } | |
668 |