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,   1e9,  
     30 	 1e10,  1e11,  1e12,  1e13,  1e14,  1e15,  1e16,  1e17,  1e18,  1e19,  
     31 	 1e20,  1e21,  1e22,  1e23,  1e24,  1e25,  1e26,  1e27,  1e28,  1e29,  
     32 	 1e30,  1e31,  1e32,  1e33,  1e34,  1e35,  1e36,  1e37,  1e38,  1e39,  
     33 	 1e40,  1e41,  1e42,  1e43,  1e44,  1e45,  1e46,  1e47,  1e48,  1e49,  
     34 	 1e50,  1e51,  1e52,  1e53,  1e54,  1e55,  1e56,  1e57,  1e58,  1e59,  
     35 	 1e60,  1e61,  1e62,  1e63,  1e64,  1e65,  1e66,  1e67,  1e68,  1e69,  
     36 	 1e70,  1e71,  1e72,  1e73,  1e74,  1e75,  1e76,  1e77,  1e78,  1e79,  
     37 	 1e80,  1e81,  1e82,  1e83,  1e84,  1e85,  1e86,  1e87,  1e88,  1e89,  
     38 	 1e90,  1e91,  1e92,  1e93,  1e94,  1e95,  1e96,  1e97,  1e98,  1e99,  
     39 	1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, 
     40 	1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, 
     41 	1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, 
     42 	1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 
     43 	1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, 
     44 	1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, 
     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 everyone up.
    130 				 * decimal is known to be 9s or we wouldn't
    131 				 * have gotten this far.  (e.g., 10000-1 => 09999)
    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 means 1)
    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 FmtSharp.
    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 zeros.
    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 after */
    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, newndigits)) {
    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 point
    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(digits, newndigits)) {
    525 				/*
    526 				 * digits was 999, is now 100; make it 1000
    527 				 */
    528 				digits[newndigits++] = '0';
    529 			newdigit:
    530 				/*
    531 				 * account for new digit
    532 				 */
    533 				if(z1)	/* 0.099 => 0.100 or 0.99 => 1.00*/
    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[ndigits-1] == '0')
    557 					ndigits--;
    558 			}
    559 		}
    560 	}
    561 
    562 	/*
    563 	 * compute width of all digits and decimal point and suffix if any
    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 point */
    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