tfltfmt.c - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
tfltfmt.c (12809B)
---
     1 /* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
     2 #include 
     3 #include 
     4 #include 
     5 #include 
     6 #include 
     7 #include 
     8 #include 
     9 #include 
    10 #include 
    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 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 }