tnew, commented fltfmt - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit f8955f181efa175573ccecc71a19464fdba50ef5
 (DIR) parent fd745e874891a9001e6774c26e145984b9db147c
 (HTM) Author: rsc <devnull@localhost>
       Date:   Mon, 22 May 2006 14:53:35 +0000
       
       new, commented fltfmt
       
       Diffstat:
         M src/lib9/fmt/fltfmt.c               |     732 +++++++++++++++++++++----------
       
       1 file changed, 509 insertions(+), 223 deletions(-)
       ---
 (DIR) diff --git a/src/lib9/fmt/fltfmt.c b/src/lib9/fmt/fltfmt.c
       t@@ -6,11 +6,12 @@
        #include <stdlib.h>
        #include <errno.h>
        #include <stdarg.h>
       -#include <ctype.h>
        #include <fmt.h>
       +#include <assert.h>
        #include "plan9.h"
        #include "fmt.h"
        #include "fmtdef.h"
       +#include "nan.h"
        
        enum
        {
       t@@ -42,8 +43,8 @@ static double pows10[] =
                1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, 
                1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, 
        };
       -
       -#define  pow10(x)  fmtpow10(x)
       +#define        npows10 ((int)(sizeof(pows10)/sizeof(pows10[0])))
       +#define        pow10(x)  fmtpow10(x)
        
        static double
        pow10(int n)
       t@@ -53,330 +54,615 @@ pow10(int n)
        
                neg = 0;
                if(n < 0){
       -                if(n < DBL_MIN_10_EXP){
       +                if(n < DBL_MIN_10_EXP)
                                return 0.;
       -                }
                        neg = 1;
                        n = -n;
       -        }else if(n > DBL_MAX_10_EXP){
       +        }else if(n > DBL_MAX_10_EXP)
                        return HUGE_VAL;
       -        }
       -        if(n < (int)(sizeof(pows10)/sizeof(pows10[0])))
       +
       +        if(n < npows10)
                        d = pows10[n];
                else{
       -                d = pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
       +                d = pows10[npows10-1];
                        for(;;){
       -                        n -= sizeof(pows10)/sizeof(pows10[0]) - 1;
       -                        if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))){
       +                        n -= npows10 - 1;
       +                        if(n < npows10){
                                        d *= pows10[n];
                                        break;
                                }
       -                        d *= pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
       +                        d *= pows10[npows10 - 1];
                        }
                }
       -        if(neg){
       +        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
       -xadd(char *a, int n, int v)
       +xadd1(char *a, int n)
        {
                char *b;
                int c;
        
       -        if(n < 0 || n >= NSIGNIF)
       +        if(n < 0 || n > NSIGNIF)
                        return 0;
       -        for(b = a+n; b >= a; b--) {
       -                c = *b + v;
       +        for(b = a+n-1; b >= a; b--) {
       +                c = *b + 1;
                        if(c <= '9') {
                                *b = c;
                                return 0;
                        }
                        *b = '0';
       -                v = 1;
                }
       -        *a = '1';        /* overflow adding */
       +        /*
       +         * 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
       -xsub(char *a, int n, int v)
       +xsub1(char *a, int n)
        {
                char *b;
                int c;
        
       -        for(b = a+n; b >= a; b--) {
       -                c = *b - v;
       +        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';
       -                v = 1;
                }
       -        *a = '9';        /* underflow subtracting */
       -        return 1;
       +        /*
       +         * 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
       -xdtoa(Fmt *fmt, char *s2, double f)
       +xfmtexp(char *p, int e, int ucase)
        {
       -        char s1[NSIGNIF+10];
       -        double g, h;
       -        int e, d, i, n;
       -        int c1, c2, c3, c4, ucase, sign, chr, prec;
       +        char se[9];
       +        int i;
        
       -        prec = FDEFLT;
       -        if(fmt->flags & FmtPrec)
       -                prec = fmt->prec;
       -        if(prec > FDIGIT)
       -                prec = FDIGIT;
       -        if(__isNaN(f)) {
       -                strcpy(s2, "NaN");
       -                return;
       -        }
       -        if(__isInf(f, 1)) {
       -                strcpy(s2, "+Inf");
       -                return;
       -        }
       -        if(__isInf(f, -1)) {
       -                strcpy(s2, "-Inf");
       -                return;
       +        *p++ = ucase ? 'E' : 'e';
       +        if(e < 0) {
       +                *p++ = '-';
       +                e = -e;
       +        } else
       +                *p++ = '+';
       +        i = 0;
       +        while(e) {
       +                se[i++] = e % 10 + '0';
       +                e /= 10;
                }
       -        sign = 0;
       +        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;
       -                sign++;
       -        }
       -        ucase = 0;
       -        chr = fmt->r;
       -        if(isupper(chr)) {
       -                ucase = 1;
       -                chr = tolower(chr);
       +                *neg = 1;
                }
        
       -        e = 0;
       -        g = f;
       -        if(g != 0) {
       -                frexp(f, &e);
       -                e = e * .301029995664;
       -                if(e >= -150 && e <= +150) {
       -                        d = 0;
       -                        h = f;
       -                } else {
       -                        d = e/2;
       -                        h = f * pow10(-d);
       -                }
       -                g = h * pow10(d-e);
       -                while(g < 1) {
       -                        e--;
       -                        g = h * pow10(d-e);
       -                }
       -                while(g >= 10) {
       -                        e++;
       -                        g = h * pow10(d-e);
       -                }
       +        /*
       +         * 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 and convert
       -         * back to get accuracy.
       +         * convert NSIGNIF digits as a first approximation.
                 */
                for(i=0; i<NSIGNIF; i++) {
       -                d = g;
       -                s1[i] = d + '0';
       -                g = (g - d) * 10;
       +                d = (int)g;
       +                s[i] = d+'0';
       +                g = (g-d) * 10;
                }
       -        s1[i] = 0;
       +        s[i] = 0;
        
                /*
       -         * try decimal rounding to eliminate 9s
       +         * adjust e because s is 314159... not 3.14159...
                 */
       -        c2 = prec + 1;
       -        if(chr == 'f')
       -                c2 += e;
       -        if(c2 >= NSIGNIF-2) {
       -                strcpy(s2, s1);
       -                d = e;
       -                s1[NSIGNIF-2] = '0';
       -                s1[NSIGNIF-1] = '0';
       -                sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
       -                g = strtod(s1, nil);
       -                if(g == f)
       -                        goto found;
       -                if(xadd(s1, NSIGNIF-3, 1)) {
       -                        e++;
       -                        sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
       -                }
       -                g = strtod(s1, nil);
       -                if(g == f)
       -                        goto found;
       -                strcpy(s1, s2);
       -                e = d;
       -        }
       +        e -= NSIGNIF-1;
       +        xfmtexp(s+NSIGNIF, e, 0);
        
                /*
       -         * convert back so s1 gets exact answer
       +         * adjust conversion until strtod(s) == f exactly.
                 */
       -        for(;;) {
       -                sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
       -                g = strtod(s1, nil);
       +        for(i=0; i<10; i++) {
       +                g = strtod(s, nil);
                        if(f > g) {
       -                        if(xadd(s1, NSIGNIF-1, 1))
       +                        if(xadd1(s, NSIGNIF)) {
       +                                /* gained a digit */
                                        e--;
       +                                xfmtexp(s+NSIGNIF, e, 0);
       +                        }
                                continue;
                        }
                        if(f < g) {
       -                        if(xsub(s1, NSIGNIF-1, 1))
       +                        if(xsub1(s, NSIGNIF)) {
       +                                /* lost a digit */
                                        e++;
       +                                xfmtexp(s+NSIGNIF, e, 0);
       +                        }
                                continue;
                        }
                        break;
                }
        
       -found:
                /*
       -         * sign
       +         * play with the decimal to try to simplify.
                 */
       -        d = 0;
       -        i = 0;
       -        if(sign)
       -                s2[d++] = '-';
       -        else if(fmt->flags & FmtSign)
       -                s2[d++] = '+';
       -        else if(fmt->flags & FmtSpace)
       -                s2[d++] = ' ';
        
                /*
       -         * copy into final place
       -         * c1 digits of leading '0'
       -         * c2 digits from conversion
       -         * c3 digits of trailing '0'
       -         * c4 digits after '.'
       +         * bump last few digits up to 9 if we can
                 */
       -        c1 = 0;
       -        c2 = prec + 1;
       -        c3 = 0;
       -        c4 = prec;
       +        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;
       +        
       +        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) {
       -        default:
       -                if(xadd(s1, c2, 5))
       -                        e++;
       +        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':
                        /*
       -                 * decide on 'e' of 'f' style convers
       +                 * convert to at most prec significant digits. (prec=0 means 1)
                         */
       -                if(xadd(s1, c2, 5))
       -                        e++;
       -                if(e >= -5 && e <= prec) {
       -                        c1 = -e - 1;
       -                        c4 = prec - e;
       -                        chr = 'h';        /* flag for 'f' style */
       +                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':
       -                if(xadd(s1, c2+e, 5))
       -                        e++;
       -                c1 = -e;
       -                if(c1 > prec)
       -                        c1 = c2;
       -                c2 += e;
       +                /*
       +                 * 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;
                }
       -
       +        
                /*
       -         * clean up c1 c2 and c3
       +         * 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(c1 < 0)
       -                c1 = 0;
       -        if(c2 < 0)
       -                c2 = 0;
       -        if(c2 > NSIGNIF) {
       -                c3 = c2-NSIGNIF;
       -                c2 = NSIGNIF;
       +        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--;
       +                        }
       +                }
                }
        
                /*
       -         * copy digits
       +         * compute width of all digits and decimal point and suffix if any
                 */
       -        while(c1 > 0) {
       -                if(c1+c2+c3 == c4)
       -                        s2[d++] = '.';
       -                s2[d++] = '0';
       -                c1--;
       -        }
       -        while(c2 > 0) {
       -                if(c2+c3 == c4)
       -                        s2[d++] = '.';
       -                s2[d++] = s1[i++];
       -                c2--;
       +        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 */
                }
       -        while(c3 > 0) {
       -                if(c3 == c4)
       -                        s2[d++] = '.';
       -                s2[d++] = '0';
       -                c3--;
       +        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;
                }
        
                /*
       -         * strip trailing '0' on g conv
       +         * format the actual field.  too bad about doing this twice.
                 */
       -        if(fmt->flags & FmtSharp) {
       -                if(0 == c4)
       -                        s2[d++] = '.';
       -        } else
       -        if(chr == 'g' || chr == 'h') {
       -                for(n=d-1; n>=0; n--)
       -                        if(s2[n] != '0')
       -                                break;
       -                for(i=n; i>=0; i--)
       -                        if(s2[i] == '.') {
       -                                d = n;
       -                                if(i != n)
       -                                        d++;
       -                                break;
       +        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 if(z2 > 0){
       +                                z2--;
       +                                c = '0';
                                }
       -        }
       -        if(chr == 'e' || chr == 'g') {
       -                if(ucase)
       -                        s2[d++] = 'E';
       -                else
       -                        s2[d++] = 'e';
       -                c1 = e;
       -                if(c1 < 0) {
       -                        s2[d++] = '-';
       -                        c1 = -c1;
       -                } else
       -                        s2[d++] = '+';
       -                if(c1 >= 100) {
       -                        s2[d++] = c1/100 + '0';
       -                        c1 = c1%100;
       +                        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 if(z2 > 0){
       +                                z2--;
       +                                c = '0';
       +                        }
       +                        FMTCHAR(fmt, t, s, c);
       +                        if(--point == 0)
       +                                for(p=dot; *p; p++)
       +                                        FMTCHAR(fmt, t, s, *p);
                        }
       -                s2[d++] = c1/10 + '0';
       -                s2[d++] = c1%10 + '0';
       +                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;
                }
       -        s2[d] = 0;
       -}
       -
       -static int
       -floatfmt(Fmt *fmt, double f)
       -{
       -        char s[341];                /* precision+exponent+sign+'.'+null */
       -
       -        xdtoa(fmt, s, f);
       -        fmt->flags &= FmtWidth|FmtLeft;
       -        __fmtcpy(fmt, s, strlen(s), strlen(s));
                return 0;
        }
        
       -int
       -__efgfmt(Fmt *f)
       -{
       -        double d;
       -
       -        d = va_arg(f->args, double);
       -        return floatfmt(f, d);
       -}