xref: /plan9/sys/src/ape/lib/fmt/fltfmt.c (revision 781103c4074deb8af160e8a0da2742ba6b29dc2b)
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
pow10(int n)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
xadd(char * a,int n,int v)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
xsub(char * a,int n,int v)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
xaddexp(char * p,int e)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*
xdodtoa(char * s1,double f,int chr,int prec,int * decpt,int * rsign)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 	 * round & adjust 'f' digits
272 	 */
273 	c2 = prec + 1;
274 	if(chr == 'f'){
275 		if(xadd(s1, c2+e, 5))
276 			e++;
277 		c2 += e;
278 		if(c2 < 0){
279 			c2 = 0;
280 			e = -prec - 1;
281 		}
282 	}else{
283 		if(xadd(s1, c2, 5))
284 			e++;
285 	}
286 	if(c2 > NSIGNIF){
287 		c2 = NSIGNIF;
288 	}
289 
290 	*decpt = e + 1;
291 
292 	/*
293 	 * terminate the converted digits
294 	 */
295 	s1[c2] = '\0';
296 	return &s1[c2];
297 }
298 
299 /*
300  * this function works like the standard dtoa, if you want it.
301  */
302 #if 0
303 static char*
304 __dtoa(double f, int mode, int ndigits, int *decpt, int *rsign, char **rve)
305 {
306 	static char s2[NSIGNIF + 10];
307 	char *es;
308 	int chr, prec;
309 
310 	switch(mode) {
311 	/* like 'e' */
312 	case 2:
313 	case 4:
314 	case 6:
315 	case 8:
316 		chr = 'e';
317 		break;
318 	/* like 'g' */
319 	case 0:
320 	case 1:
321 	default:
322 		chr = 'g';
323 		break;
324 	/* like 'f' */
325 	case 3:
326 	case 5:
327 	case 7:
328 	case 9:
329 		chr = 'f';
330 		break;
331 	}
332 
333 	if(chr != 'f' && ndigits){
334 		ndigits--;
335 	}
336 	prec = ndigits;
337 	if(prec > NSIGNIF)
338 		prec = NSIGNIF;
339 	if(ndigits == 0)
340 		prec = NSIGNIF;
341 	es = xdodtoa(s2, f, chr, prec, decpt, rsign);
342 
343 	/*
344 	 * strip trailing 0
345 	 */
346 	for(; es > s2 + 1; es--){
347 		if(es[-1] != '0'){
348 			break;
349 		}
350 	}
351 	*es = '\0';
352 	if(rve != NULL)
353 		*rve = es;
354 	return s2;
355 }
356 #endif
357 
358 static int
fmtzdotpad(Fmt * f,int n,int pt)359 fmtzdotpad(Fmt *f, int n, int pt)
360 {
361 	char *t, *s;
362 	int i;
363 	Rune *rt, *rs;
364 
365 	if(f->runes){
366 		rt = (Rune*)f->to;
367 		rs = (Rune*)f->stop;
368 		for(i = 0; i < n; i++){
369 			if(i == pt){
370 				FMTRCHAR(f, rt, rs, '.');
371 			}
372 			FMTRCHAR(f, rt, rs, '0');
373 		}
374 		f->nfmt += rt - (Rune*)f->to;
375 		f->to = rt;
376 	}else{
377 		t = (char*)f->to;
378 		s = (char*)f->stop;
379 		for(i = 0; i < n; i++){
380 			if(i == pt){
381 				FMTCHAR(f, t, s, '.');
382 			}
383 			FMTCHAR(f, t, s, '0');
384 		}
385 		f->nfmt += t - (char *)f->to;
386 		f->to = t;
387 	}
388 	return 0;
389 }
390 
391 int
__efgfmt(Fmt * fmt)392 __efgfmt(Fmt *fmt)
393 {
394 	double f;
395 	char s1[NSIGNIF+10];
396 	int e, d, n;
397 	int c1, c2, c3, c4, ucase, sign, chr, prec, fl;
398 
399 	f = va_arg(fmt->args, double);
400 	prec = FDEFLT;
401 	fl = fmt->flags;
402 	fmt->flags = 0;
403 	if(fl & FmtPrec)
404 		prec = fmt->prec;
405 	chr = fmt->r;
406 	ucase = 0;
407 	if(chr == 'E'){
408 		chr = 'e';
409 		ucase = 1;
410 	}else if(chr == 'F'){
411 		chr = 'f';
412 		ucase = 1;
413 	}else if(chr == 'G'){
414 		chr = 'g';
415 		ucase = 1;
416 	}
417 	if(prec > 0 && chr == 'g')
418 		prec--;
419 	if(prec < 0)
420 		prec = 0;
421 
422 	xdodtoa(s1, f, chr, prec, &e, &sign);
423 	e--;
424 	if(*s1 == 'i' || *s1 == 'n'){
425 		if(ucase){
426 			if(*s1 == 'i'){
427 				strcpy(s1, "INF");
428 			}else{
429 				strcpy(s1, "NAN");
430 			}
431 		}
432 		fmt->flags = fl & (FmtWidth|FmtLeft);
433 		return __fmtcpy(fmt, (const void*)s1, 3, 3);
434 	}
435 
436 	/*
437 	 * copy into final place
438 	 * c1 digits of leading '0'
439 	 * c2 digits from conversion
440 	 * c3 digits of trailing '0'
441 	 * c4 digits after '.'
442 	 */
443 	c1 = 0;
444 	c2 = prec + 1;
445 	c3 = 0;
446 	c4 = prec;
447 	switch(chr) {
448 	default:
449 		chr = 'e';
450 		break;
451 	case 'g':
452 		/*
453 		 * decide on 'e' of 'f' style convers
454 		 */
455 		if(e >= -4 && e <= prec) {
456 			c1 = -e;
457 			c4 = prec - e;
458 			chr = 'h';	/* flag for 'f' style */
459 		}
460 		break;
461 	case 'f':
462 		c1 = -e;
463 		if(c1 > prec)
464 			c1 = prec + 1;
465 		c2 += e;
466 		break;
467 	}
468 
469 	/*
470 	 * clean up c1 c2 and c3
471 	 */
472 	if(c1 < 0)
473 		c1 = 0;
474 	if(c2 < 0)
475 		c2 = 0;
476 	if(c2 > NSIGNIF) {
477 		c3 = c2-NSIGNIF;
478 		c2 = NSIGNIF;
479 	}
480 
481 	/*
482 	 * trim trailing zeros for %g
483 	 */
484 	if(!(fl & FmtSharp)
485 	&& (chr == 'g' || chr == 'h')){
486 		if(c4 >= c3){
487 			c4 -= c3;
488 			c3 = 0;
489 		}else{
490 			c3 -= c4;
491 			c4 = 0;
492 		}
493 		while(c4 && c2 > 1 && s1[c2 - 1] == '0'){
494 			c4--;
495 			c2--;
496 		}
497 	}
498 
499 	/*
500 	 * calculate the total length
501 	 */
502 	n = c1 + c2 + c3;
503 	if(sign || (fl & (FmtSign|FmtSpace)))
504 		n++;
505 	if(c4 || (fl & FmtSharp)){
506 		n++;
507 	}
508 	if(chr == 'e' || chr == 'g'){
509 		n += 4;
510 		if(e >= 100)
511 			n++;
512 	}
513 
514 	/*
515 	 * pad to width if right justified
516 	 */
517 	if((fl & (FmtWidth|FmtLeft)) == FmtWidth && n < fmt->width){
518 		if(fl & FmtZero){
519 			c1 += fmt->width - n;
520 		}else{
521 			if(__fmtpad(fmt, fmt->width - n) < 0){
522 				return -1;
523 			}
524 		}
525 	}
526 
527 	/*
528 	 * sign
529 	 */
530 	d = 0;
531 	if(sign)
532 		d = '-';
533 	else if(fl & FmtSign)
534 		d = '+';
535 	else if(fl & FmtSpace)
536 		d = ' ';
537 	if(d && fmtrune(fmt, d) < 0){
538 		return -1;
539 	}
540 
541 	/*
542 	 * copy digits
543 	 */
544 	c4 = c1 + c2 + c3 - c4;
545 	if(c1 > 0){
546 		if(fmtzdotpad(fmt, c1, c4) < 0){
547 			return -1;
548 		}
549 		c4 -= c1;
550 	}
551 	d = 0;
552 	if(c4 >= 0 && c4 < c2){
553 		if(__fmtcpy(fmt, s1, c4, c4) < 0 || fmtrune(fmt, '.') < 0)
554 			return -1;
555 		d = c4;
556 		c2 -= c4;
557 		c4 = -1;
558 	}
559 	if(__fmtcpy(fmt, (const void*)(s1 + d), c2, c2) < 0){
560 		return -1;
561 	}
562 	c4 -= c2;
563 	if(c3 > 0){
564 		if(fmtzdotpad(fmt, c3, c4) < 0){
565 			return -1;
566 		}
567 		c4 -= c3;
568 	}
569 
570 	/*
571 	 * strip trailing '0' on g conv
572 	 */
573 	if((fl & FmtSharp) && c4 == 0 && fmtrune(fmt, '.') < 0){
574 		return -1;
575 	}
576 	if(chr == 'e' || chr == 'g') {
577 		d = 0;
578 		if(ucase)
579 			s1[d++] = 'E';
580 		else
581 			s1[d++] = 'e';
582 		c1 = e;
583 		if(c1 < 0) {
584 			s1[d++] = '-';
585 			c1 = -c1;
586 		} else
587 			s1[d++] = '+';
588 		if(c1 >= 100) {
589 			s1[d++] = c1/100 + '0';
590 			c1 = c1%100;
591 		}
592 		s1[d++] = c1/10 + '0';
593 		s1[d++] = c1%10 + '0';
594 		if(__fmtcpy(fmt, s1, d, d) < 0){
595 			return -1;
596 		}
597 	}
598 	if((fl & (FmtWidth|FmtLeft)) == (FmtWidth|FmtLeft) && n < fmt->width){
599 		if(__fmtpad(fmt, fmt->width - n) < 0){
600 			return -1;
601 		}
602 	}
603 	return 0;
604 }
605