fltfmt.c - 9base - revived minimalist port of Plan 9 userland to Unix
 (HTM) git clone git://git.suckless.org/9base
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) 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