wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

fltfmt.c (13495B)


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