diff options
Diffstat (limited to 'src/lib9/fmt/fltfmt.c')
-rw-r--r-- | src/lib9/fmt/fltfmt.c | 679 |
1 files changed, 0 insertions, 679 deletions
diff --git a/src/lib9/fmt/fltfmt.c b/src/lib9/fmt/fltfmt.c deleted file mode 100644 index 9f3f3edab..000000000 --- a/src/lib9/fmt/fltfmt.c +++ /dev/null @@ -1,679 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson, - * with contributions from Mike Burrows and Sean Dorward. - * - * Copyright (c) 2002-2006 by Lucent Technologies. - * Portions Copyright (c) 2004 Google Inc. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES - * NOR GOOGLE INC MAKE ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING - * THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ - -/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ -#include <u.h> -#include <errno.h> -#include <libc.h> -#include "fmtdef.h" - -enum -{ - FDIGIT = 30, - FDEFLT = 6, - NSIGNIF = 17 -}; - -/* - * first few powers of 10, enough for about 1/2 of the - * total space for doubles. - */ -static double pows10[] = -{ - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, - 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, - 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, - 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, - 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, - 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, - 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, - 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, - 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, - 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, - 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, - 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, - 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, - 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, -}; - -#undef pow10 -#define npows10 ((int)(sizeof(pows10)/sizeof(pows10[0]))) -#define pow10(x) fmtpow10(x) - -static double -pow10(int n) -{ - double d; - int neg; - - neg = 0; - if(n < 0){ - neg = 1; - n = -n; - } - - if(n < npows10) - d = pows10[n]; - else{ - d = pows10[npows10-1]; - for(;;){ - n -= npows10 - 1; - if(n < npows10){ - d *= pows10[n]; - break; - } - d *= pows10[npows10 - 1]; - } - } - if(neg) - return 1./d; - return d; -} - -/* - * add 1 to the decimal integer string a of length n. - * if 99999 overflows into 10000, return 1 to tell caller - * to move the virtual decimal point. - */ -static int -xadd1(char *a, int n) -{ - char *b; - int c; - - if(n < 0 || n > NSIGNIF) - return 0; - for(b = a+n-1; b >= a; b--) { - c = *b + 1; - if(c <= '9') { - *b = c; - return 0; - } - *b = '0'; - } - /* - * need to overflow adding digit. - * shift number down and insert 1 at beginning. - * decimal is known to be 0s or we wouldn't - * have gotten this far. (e.g., 99999+1 => 00000) - */ - a[0] = '1'; - return 1; -} - -/* - * subtract 1 from the decimal integer string a. - * if 10000 underflows into 09999, make it 99999 - * and return 1 to tell caller to move the virtual - * decimal point. this way, xsub1 is inverse of xadd1. - */ -static int -xsub1(char *a, int n) -{ - char *b; - int c; - - if(n < 0 || n > NSIGNIF) - return 0; - for(b = a+n-1; b >= a; b--) { - c = *b - 1; - if(c >= '0') { - if(c == '0' && b == a) { - /* - * just zeroed the top digit; shift everyone up. - * decimal is known to be 9s or we wouldn't - * have gotten this far. (e.g., 10000-1 => 09999) - */ - *b = '9'; - return 1; - } - *b = c; - return 0; - } - *b = '9'; - } - /* - * can't get here. the number a is always normalized - * so that it has a nonzero first digit. - */ - abort(); -} - -/* - * format exponent like sprintf(p, "e%+02d", e) - */ -static void -xfmtexp(char *p, int e, int ucase) -{ - char se[9]; - int i; - - *p++ = ucase ? 'E' : 'e'; - if(e < 0) { - *p++ = '-'; - e = -e; - } else - *p++ = '+'; - i = 0; - while(e) { - se[i++] = e % 10 + '0'; - e /= 10; - } - while(i < 2) - se[i++] = '0'; - while(i > 0) - *p++ = se[--i]; - *p++ = '\0'; -} - -/* - * compute decimal integer m, exp such that: - * f = m*10^exp - * m is as short as possible with losing exactness - * assumes special cases (NaN, +Inf, -Inf) have been handled. - */ -static void -xdtoa(double f, char *s, int *exp, int *neg, int *ns) -{ - int c, d, e2, e, ee, i, ndigit, oerrno; - char tmp[NSIGNIF+10]; - double g; - - oerrno = errno; /* in case strtod smashes errno */ - - /* - * make f non-negative. - */ - *neg = 0; - if(f < 0) { - f = -f; - *neg = 1; - } - - /* - * must handle zero specially. - */ - if(f == 0){ - *exp = 0; - s[0] = '0'; - s[1] = '\0'; - *ns = 1; - return; - } - - /* - * find g,e such that f = g*10^e. - * guess 10-exponent using 2-exponent, then fine tune. - */ - frexp(f, &e2); - e = (int)(e2 * .301029995664); - g = f * pow10(-e); - while(g < 1) { - e--; - g = f * pow10(-e); - } - while(g >= 10) { - e++; - g = f * pow10(-e); - } - - /* - * convert NSIGNIF digits as a first approximation. - */ - for(i=0; i<NSIGNIF; i++) { - d = (int)g; - s[i] = d+'0'; - g = (g-d) * 10; - } - s[i] = 0; - - /* - * adjust e because s is 314159... not 3.14159... - */ - e -= NSIGNIF-1; - xfmtexp(s+NSIGNIF, e, 0); - - /* - * adjust conversion until strtod(s) == f exactly. - */ - for(i=0; i<10; i++) { - g = strtod(s, nil); - if(f > g) { - if(xadd1(s, NSIGNIF)) { - /* gained a digit */ - e--; - xfmtexp(s+NSIGNIF, e, 0); - } - continue; - } - if(f < g) { - if(xsub1(s, NSIGNIF)) { - /* lost a digit */ - e++; - xfmtexp(s+NSIGNIF, e, 0); - } - continue; - } - break; - } - - /* - * play with the decimal to try to simplify. - */ - - /* - * bump last few digits up to 9 if we can - */ - for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) { - c = s[i]; - if(c != '9') { - s[i] = '9'; - g = strtod(s, nil); - if(g != f) { - s[i] = c; - break; - } - } - } - - /* - * add 1 in hopes of turning 9s to 0s - */ - if(s[NSIGNIF-1] == '9') { - strcpy(tmp, s); - ee = e; - if(xadd1(tmp, NSIGNIF)) { - ee--; - xfmtexp(tmp+NSIGNIF, ee, 0); - } - g = strtod(tmp, nil); - if(g == f) { - strcpy(s, tmp); - e = ee; - } - } - - /* - * bump last few digits down to 0 as we can. - */ - for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) { - c = s[i]; - if(c != '0') { - s[i] = '0'; - g = strtod(s, nil); - if(g != f) { - s[i] = c; - break; - } - } - } - - /* - * remove trailing zeros. - */ - ndigit = NSIGNIF; - while(ndigit > 1 && s[ndigit-1] == '0'){ - e++; - --ndigit; - } - s[ndigit] = 0; - *exp = e; - *ns = ndigit; - errno = oerrno; -} - -#ifdef PLAN9PORT -static char *special[] = { "NaN", "NaN", "+Inf", "+Inf", "-Inf", "-Inf" }; -#else -static char *special[] = { "nan", "NAN", "inf", "INF", "-inf", "-INF" }; -#endif - -int -__efgfmt(Fmt *fmt) -{ - char buf[NSIGNIF+10], *dot, *digits, *p, *s, suf[10], *t; - double f; - int c, chr, dotwid, e, exp, fl, ndigits, neg, newndigits; - int pad, point, prec, realchr, sign, sufwid, ucase, wid, z1, z2; - Rune r, *rs, *rt; - - if(fmt->flags&FmtLong) - f = va_arg(fmt->args, long double); - else - f = va_arg(fmt->args, double); - - /* - * extract formatting flags - */ - fl = fmt->flags; - fmt->flags = 0; - prec = FDEFLT; - if(fl & FmtPrec) - prec = fmt->prec; - chr = fmt->r; - ucase = 0; - switch(chr) { - case 'A': - case 'E': - case 'F': - case 'G': - chr += 'a'-'A'; - ucase = 1; - break; - } - - /* - * pick off special numbers. - */ - if(__isNaN(f)) { - s = special[0+ucase]; - special: - fmt->flags = fl & (FmtWidth|FmtLeft); - return __fmtcpy(fmt, s, strlen(s), strlen(s)); - } - if(__isInf(f, 1)) { - s = special[2+ucase]; - goto special; - } - if(__isInf(f, -1)) { - s = special[4+ucase]; - goto special; - } - - /* - * get exact representation. - */ - digits = buf; - xdtoa(f, digits, &exp, &neg, &ndigits); - - /* - * get locale's decimal point. - */ - dot = fmt->decimal; - if(dot == nil) - dot = "."; - dotwid = utflen(dot); - - /* - * now the formatting fun begins. - * compute parameters for actual fmt: - * - * pad: number of spaces to insert before/after field. - * z1: number of zeros to insert before digits - * z2: number of zeros to insert after digits - * point: number of digits to print before decimal point - * ndigits: number of digits to use from digits[] - * suf: trailing suffix, like "e-5" - */ - realchr = chr; - switch(chr){ - case 'g': - /* - * convert to at most prec significant digits. (prec=0 means 1) - */ - if(prec == 0) - prec = 1; - if(ndigits > prec) { - if(digits[prec] >= '5' && xadd1(digits, prec)) - exp++; - exp += ndigits-prec; - ndigits = prec; - } - - /* - * extra rules for %g (implemented below): - * trailing zeros removed after decimal unless FmtSharp. - * decimal point only if digit follows. - */ - - /* fall through to %e */ - default: - case 'e': - /* - * one significant digit before decimal, no leading zeros. - */ - point = 1; - z1 = 0; - - /* - * decimal point is after ndigits digits right now. - * slide to be after first. - */ - e = exp + (ndigits-1); - - /* - * if this is %g, check exponent and convert prec - */ - if(realchr == 'g') { - if(-4 <= e && e < prec) - goto casef; - prec--; /* one digit before decimal; rest after */ - } - - /* - * compute trailing zero padding or truncate digits. - */ - if(1+prec >= ndigits) - z2 = 1+prec - ndigits; - else { - /* - * truncate digits - */ - assert(realchr != 'g'); - newndigits = 1+prec; - if(digits[newndigits] >= '5' && xadd1(digits, newndigits)) { - /* - * had 999e4, now have 100e5 - */ - e++; - } - ndigits = newndigits; - z2 = 0; - } - xfmtexp(suf, e, ucase); - sufwid = strlen(suf); - break; - - casef: - case 'f': - /* - * determine where digits go with respect to decimal point - */ - if(ndigits+exp > 0) { - point = ndigits+exp; - z1 = 0; - } else { - point = 1; - z1 = 1 + -(ndigits+exp); - } - - /* - * %g specifies prec = number of significant digits - * convert to number of digits after decimal point - */ - if(realchr == 'g') - prec += z1 - point; - - /* - * compute trailing zero padding or truncate digits. - */ - if(point+prec >= z1+ndigits) - z2 = point+prec - (z1+ndigits); - else { - /* - * truncate digits - */ - assert(realchr != 'g'); - newndigits = point+prec - z1; - if(newndigits < 0) { - z1 += newndigits; - newndigits = 0; - } else if(newndigits == 0) { - /* perhaps round up */ - if(digits[0] >= '5'){ - digits[0] = '1'; - newndigits = 1; - goto newdigit; - } - } else if(digits[newndigits] >= '5' && xadd1(digits, newndigits)) { - /* - * digits was 999, is now 100; make it 1000 - */ - digits[newndigits++] = '0'; - newdigit: - /* - * account for new digit - */ - if(z1) /* 0.099 => 0.100 or 0.99 => 1.00*/ - z1--; - else /* 9.99 => 10.00 */ - point++; - } - z2 = 0; - ndigits = newndigits; - } - sufwid = 0; - break; - } - - /* - * if %g is given without FmtSharp, remove trailing zeros. - * must do after truncation, so that e.g. print %.3g 1.001 - * produces 1, not 1.00. sorry, but them's the rules. - */ - if(realchr == 'g' && !(fl & FmtSharp)) { - if(z1+ndigits+z2 >= point) { - if(z1+ndigits < point) - z2 = point - (z1+ndigits); - else{ - z2 = 0; - while(z1+ndigits > point && digits[ndigits-1] == '0') - ndigits--; - } - } - } - - /* - * compute width of all digits and decimal point and suffix if any - */ - wid = z1+ndigits+z2; - if(wid > point) - wid += dotwid; - else if(wid == point){ - if(fl & FmtSharp) - wid += dotwid; - else - point++; /* do not print any decimal point */ - } - wid += sufwid; - - /* - * determine sign - */ - sign = 0; - if(neg) - sign = '-'; - else if(fl & FmtSign) - sign = '+'; - else if(fl & FmtSpace) - sign = ' '; - if(sign) - wid++; - - /* - * compute padding - */ - pad = 0; - if((fl & FmtWidth) && fmt->width > wid) - pad = fmt->width - wid; - if(pad && !(fl & FmtLeft) && (fl & FmtZero)){ - z1 += pad; - point += pad; - pad = 0; - } - - /* - * format the actual field. too bad about doing this twice. - */ - if(fmt->runes){ - if(pad && !(fl & FmtLeft) && __rfmtpad(fmt, pad) < 0) - return -1; - rt = (Rune*)fmt->to; - rs = (Rune*)fmt->stop; - if(sign) - FMTRCHAR(fmt, rt, rs, sign); - while(z1>0 || ndigits>0 || z2>0) { - if(z1 > 0){ - z1--; - c = '0'; - }else if(ndigits > 0){ - ndigits--; - c = *digits++; - }else{ - z2--; - c = '0'; - } - FMTRCHAR(fmt, rt, rs, c); - if(--point == 0) { - for(p = dot; *p; ){ - p += chartorune(&r, p); - FMTRCHAR(fmt, rt, rs, r); - } - } - } - fmt->nfmt += rt - (Rune*)fmt->to; - fmt->to = rt; - if(sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0) - return -1; - if(pad && (fl & FmtLeft) && __rfmtpad(fmt, pad) < 0) - return -1; - }else{ - if(pad && !(fl & FmtLeft) && __fmtpad(fmt, pad) < 0) - return -1; - t = (char*)fmt->to; - s = (char*)fmt->stop; - if(sign) - FMTCHAR(fmt, t, s, sign); - while(z1>0 || ndigits>0 || z2>0) { - if(z1 > 0){ - z1--; - c = '0'; - }else if(ndigits > 0){ - ndigits--; - c = *digits++; - }else{ - z2--; - c = '0'; - } - FMTCHAR(fmt, t, s, c); - if(--point == 0) - for(p=dot; *p; p++) - FMTCHAR(fmt, t, s, *p); - } - fmt->nfmt += t - (char*)fmt->to; - fmt->to = t; - if(sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0) - return -1; - if(pad && (fl & FmtLeft) && __fmtpad(fmt, pad) < 0) - return -1; - } - return 0; -} - |