1*8ccd4a63SDavid du Colombier #include <u.h> 2*8ccd4a63SDavid du Colombier #include <libc.h> 3*8ccd4a63SDavid du Colombier #include <ctype.h> 4*8ccd4a63SDavid du Colombier #include "fmtdef.h" 5*8ccd4a63SDavid du Colombier 6*8ccd4a63SDavid du Colombier enum 7*8ccd4a63SDavid du Colombier { 8*8ccd4a63SDavid du Colombier FDIGIT = 30, 9*8ccd4a63SDavid du Colombier FDEFLT = 6, 10*8ccd4a63SDavid du Colombier NSIGNIF = 17, 11*8ccd4a63SDavid du Colombier }; 12*8ccd4a63SDavid du Colombier 13*8ccd4a63SDavid du Colombier static int 14*8ccd4a63SDavid du Colombier xadd(char *a, int n, int v) 15*8ccd4a63SDavid du Colombier { 16*8ccd4a63SDavid du Colombier char *b; 17*8ccd4a63SDavid du Colombier int c; 18*8ccd4a63SDavid du Colombier 19*8ccd4a63SDavid du Colombier if(n < 0 || n >= NSIGNIF) 20*8ccd4a63SDavid du Colombier return 0; 21*8ccd4a63SDavid du Colombier for(b = a+n; b >= a; b--) { 22*8ccd4a63SDavid du Colombier c = *b + v; 23*8ccd4a63SDavid du Colombier if(c <= '9') { 24*8ccd4a63SDavid du Colombier *b = c; 25*8ccd4a63SDavid du Colombier return 0; 26*8ccd4a63SDavid du Colombier } 27*8ccd4a63SDavid du Colombier *b = '0'; 28*8ccd4a63SDavid du Colombier v = 1; 29*8ccd4a63SDavid du Colombier } 30*8ccd4a63SDavid du Colombier *a = '1'; // overflow adding 31*8ccd4a63SDavid du Colombier return 1; 32*8ccd4a63SDavid du Colombier } 33*8ccd4a63SDavid du Colombier 34*8ccd4a63SDavid du Colombier static int 35*8ccd4a63SDavid du Colombier xsub(char *a, int n, int v) 36*8ccd4a63SDavid du Colombier { 37*8ccd4a63SDavid du Colombier char *b; 38*8ccd4a63SDavid du Colombier int c; 39*8ccd4a63SDavid du Colombier 40*8ccd4a63SDavid du Colombier for(b = a+n; b >= a; b--) { 41*8ccd4a63SDavid du Colombier c = *b - v; 42*8ccd4a63SDavid du Colombier if(c >= '0') { 43*8ccd4a63SDavid du Colombier *b = c; 44*8ccd4a63SDavid du Colombier return 0; 45*8ccd4a63SDavid du Colombier } 46*8ccd4a63SDavid du Colombier *b = '9'; 47*8ccd4a63SDavid du Colombier v = 1; 48*8ccd4a63SDavid du Colombier } 49*8ccd4a63SDavid du Colombier *a = '9'; // underflow subtracting 50*8ccd4a63SDavid du Colombier return 1; 51*8ccd4a63SDavid du Colombier } 52*8ccd4a63SDavid du Colombier 53*8ccd4a63SDavid du Colombier static void 54*8ccd4a63SDavid du Colombier xdtoa(Fmt *fmt, char *s2, double f) 55*8ccd4a63SDavid du Colombier { 56*8ccd4a63SDavid du Colombier char s1[NSIGNIF+10]; 57*8ccd4a63SDavid du Colombier double g, h; 58*8ccd4a63SDavid du Colombier int e, d, i, n; 59*8ccd4a63SDavid du Colombier int c1, c2, c3, c4, ucase, sign, chr, prec; 60*8ccd4a63SDavid du Colombier 61*8ccd4a63SDavid du Colombier prec = FDEFLT; 62*8ccd4a63SDavid du Colombier if(fmt->flags & FmtPrec) 63*8ccd4a63SDavid du Colombier prec = fmt->prec; 64*8ccd4a63SDavid du Colombier if(prec > FDIGIT) 65*8ccd4a63SDavid du Colombier prec = FDIGIT; 66*8ccd4a63SDavid du Colombier if(__isNaN(f)) { 67*8ccd4a63SDavid du Colombier strcpy(s2, "NaN"); 68*8ccd4a63SDavid du Colombier return; 69*8ccd4a63SDavid du Colombier } 70*8ccd4a63SDavid du Colombier if(__isInf(f, 1)) { 71*8ccd4a63SDavid du Colombier strcpy(s2, "+Inf"); 72*8ccd4a63SDavid du Colombier return; 73*8ccd4a63SDavid du Colombier } 74*8ccd4a63SDavid du Colombier if(__isInf(f, -1)) { 75*8ccd4a63SDavid du Colombier strcpy(s2, "-Inf"); 76*8ccd4a63SDavid du Colombier return; 77*8ccd4a63SDavid du Colombier } 78*8ccd4a63SDavid du Colombier sign = 0; 79*8ccd4a63SDavid du Colombier if(f < 0) { 80*8ccd4a63SDavid du Colombier f = -f; 81*8ccd4a63SDavid du Colombier sign++; 82*8ccd4a63SDavid du Colombier } 83*8ccd4a63SDavid du Colombier ucase = 0; 84*8ccd4a63SDavid du Colombier chr = fmt->r; 85*8ccd4a63SDavid du Colombier if(isupper(chr)) { 86*8ccd4a63SDavid du Colombier ucase = 1; 87*8ccd4a63SDavid du Colombier chr = tolower(chr); 88*8ccd4a63SDavid du Colombier } 89*8ccd4a63SDavid du Colombier 90*8ccd4a63SDavid du Colombier e = 0; 91*8ccd4a63SDavid du Colombier g = f; 92*8ccd4a63SDavid du Colombier if(g != 0) { 93*8ccd4a63SDavid du Colombier frexp(f, &e); 94*8ccd4a63SDavid du Colombier e = e * .301029995664; 95*8ccd4a63SDavid du Colombier if(e >= -150 && e <= +150) { 96*8ccd4a63SDavid du Colombier d = 0; 97*8ccd4a63SDavid du Colombier h = f; 98*8ccd4a63SDavid du Colombier } else { 99*8ccd4a63SDavid du Colombier d = e/2; 100*8ccd4a63SDavid du Colombier h = f * pow10(-d); 101*8ccd4a63SDavid du Colombier } 102*8ccd4a63SDavid du Colombier g = h * pow10(d-e); 103*8ccd4a63SDavid du Colombier while(g < 1) { 104*8ccd4a63SDavid du Colombier e--; 105*8ccd4a63SDavid du Colombier g = h * pow10(d-e); 106*8ccd4a63SDavid du Colombier } 107*8ccd4a63SDavid du Colombier while(g >= 10) { 108*8ccd4a63SDavid du Colombier e++; 109*8ccd4a63SDavid du Colombier g = h * pow10(d-e); 110*8ccd4a63SDavid du Colombier } 111*8ccd4a63SDavid du Colombier } 112*8ccd4a63SDavid du Colombier 113*8ccd4a63SDavid du Colombier /* 114*8ccd4a63SDavid du Colombier * convert NSIGNIF digits and convert 115*8ccd4a63SDavid du Colombier * back to get accuracy. 116*8ccd4a63SDavid du Colombier */ 117*8ccd4a63SDavid du Colombier for(i=0; i<NSIGNIF; i++) { 118*8ccd4a63SDavid du Colombier d = g; 119*8ccd4a63SDavid du Colombier s1[i] = d + '0'; 120*8ccd4a63SDavid du Colombier g = (g - d) * 10; 121*8ccd4a63SDavid du Colombier } 122*8ccd4a63SDavid du Colombier s1[i] = 0; 123*8ccd4a63SDavid du Colombier 124*8ccd4a63SDavid du Colombier /* 125*8ccd4a63SDavid du Colombier * try decimal rounding to eliminate 9s 126*8ccd4a63SDavid du Colombier */ 127*8ccd4a63SDavid du Colombier c2 = prec + 1; 128*8ccd4a63SDavid du Colombier if(chr == 'f') 129*8ccd4a63SDavid du Colombier c2 += e; 130*8ccd4a63SDavid du Colombier if(c2 >= NSIGNIF-2) { 131*8ccd4a63SDavid du Colombier strcpy(s2, s1); 132*8ccd4a63SDavid du Colombier d = e; 133*8ccd4a63SDavid du Colombier s1[NSIGNIF-2] = '0'; 134*8ccd4a63SDavid du Colombier s1[NSIGNIF-1] = '0'; 135*8ccd4a63SDavid du Colombier sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1); 136*8ccd4a63SDavid du Colombier g = strtod(s1, nil); 137*8ccd4a63SDavid du Colombier if(g == f) 138*8ccd4a63SDavid du Colombier goto found; 139*8ccd4a63SDavid du Colombier if(xadd(s1, NSIGNIF-3, 1)) { 140*8ccd4a63SDavid du Colombier e++; 141*8ccd4a63SDavid du Colombier sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1); 142*8ccd4a63SDavid du Colombier } 143*8ccd4a63SDavid du Colombier g = strtod(s1, nil); 144*8ccd4a63SDavid du Colombier if(g == f) 145*8ccd4a63SDavid du Colombier goto found; 146*8ccd4a63SDavid du Colombier strcpy(s1, s2); 147*8ccd4a63SDavid du Colombier e = d; 148*8ccd4a63SDavid du Colombier } 149*8ccd4a63SDavid du Colombier 150*8ccd4a63SDavid du Colombier /* 151*8ccd4a63SDavid du Colombier * convert back so s1 gets exact answer 152*8ccd4a63SDavid du Colombier */ 153*8ccd4a63SDavid du Colombier for(;;) { 154*8ccd4a63SDavid du Colombier sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1); 155*8ccd4a63SDavid du Colombier g = strtod(s1, nil); 156*8ccd4a63SDavid du Colombier if(f > g) { 157*8ccd4a63SDavid du Colombier if(xadd(s1, NSIGNIF-1, 1)) 158*8ccd4a63SDavid du Colombier e--; 159*8ccd4a63SDavid du Colombier continue; 160*8ccd4a63SDavid du Colombier } 161*8ccd4a63SDavid du Colombier if(f < g) { 162*8ccd4a63SDavid du Colombier if(xsub(s1, NSIGNIF-1, 1)) 163*8ccd4a63SDavid du Colombier e++; 164*8ccd4a63SDavid du Colombier continue; 165*8ccd4a63SDavid du Colombier } 166*8ccd4a63SDavid du Colombier break; 167*8ccd4a63SDavid du Colombier } 168*8ccd4a63SDavid du Colombier 169*8ccd4a63SDavid du Colombier found: 170*8ccd4a63SDavid du Colombier /* 171*8ccd4a63SDavid du Colombier * sign 172*8ccd4a63SDavid du Colombier */ 173*8ccd4a63SDavid du Colombier d = 0; 174*8ccd4a63SDavid du Colombier i = 0; 175*8ccd4a63SDavid du Colombier if(sign) 176*8ccd4a63SDavid du Colombier s2[d++] = '-'; 177*8ccd4a63SDavid du Colombier else if(fmt->flags & FmtSign) 178*8ccd4a63SDavid du Colombier s2[d++] = '+'; 179*8ccd4a63SDavid du Colombier else if(fmt->flags & FmtSpace) 180*8ccd4a63SDavid du Colombier s2[d++] = ' '; 181*8ccd4a63SDavid du Colombier 182*8ccd4a63SDavid du Colombier /* 183*8ccd4a63SDavid du Colombier * copy into final place 184*8ccd4a63SDavid du Colombier * c1 digits of leading '0' 185*8ccd4a63SDavid du Colombier * c2 digits from conversion 186*8ccd4a63SDavid du Colombier * c3 digits of trailing '0' 187*8ccd4a63SDavid du Colombier * c4 digits after '.' 188*8ccd4a63SDavid du Colombier */ 189*8ccd4a63SDavid du Colombier c1 = 0; 190*8ccd4a63SDavid du Colombier c2 = prec + 1; 191*8ccd4a63SDavid du Colombier c3 = 0; 192*8ccd4a63SDavid du Colombier c4 = prec; 193*8ccd4a63SDavid du Colombier switch(chr) { 194*8ccd4a63SDavid du Colombier default: 195*8ccd4a63SDavid du Colombier if(xadd(s1, c2, 5)) 196*8ccd4a63SDavid du Colombier e++; 197*8ccd4a63SDavid du Colombier break; 198*8ccd4a63SDavid du Colombier case 'g': 199*8ccd4a63SDavid du Colombier /* 200*8ccd4a63SDavid du Colombier * decide on 'e' of 'f' style convers 201*8ccd4a63SDavid du Colombier */ 202*8ccd4a63SDavid du Colombier if(xadd(s1, c2, 5)) 203*8ccd4a63SDavid du Colombier e++; 204*8ccd4a63SDavid du Colombier if(e >= -5 && e <= prec) { 205*8ccd4a63SDavid du Colombier c1 = -e - 1; 206*8ccd4a63SDavid du Colombier c4 = prec - e; 207*8ccd4a63SDavid du Colombier chr = 'h'; // flag for 'f' style 208*8ccd4a63SDavid du Colombier } 209*8ccd4a63SDavid du Colombier break; 210*8ccd4a63SDavid du Colombier case 'f': 211*8ccd4a63SDavid du Colombier if(xadd(s1, c2+e, 5)) 212*8ccd4a63SDavid du Colombier e++; 213*8ccd4a63SDavid du Colombier c1 = -e; 214*8ccd4a63SDavid du Colombier if(c1 > prec) 215*8ccd4a63SDavid du Colombier c1 = c2; 216*8ccd4a63SDavid du Colombier c2 += e; 217*8ccd4a63SDavid du Colombier break; 218*8ccd4a63SDavid du Colombier } 219*8ccd4a63SDavid du Colombier 220*8ccd4a63SDavid du Colombier /* 221*8ccd4a63SDavid du Colombier * clean up c1 c2 and c3 222*8ccd4a63SDavid du Colombier */ 223*8ccd4a63SDavid du Colombier if(c1 < 0) 224*8ccd4a63SDavid du Colombier c1 = 0; 225*8ccd4a63SDavid du Colombier if(c2 < 0) 226*8ccd4a63SDavid du Colombier c2 = 0; 227*8ccd4a63SDavid du Colombier if(c2 > NSIGNIF) { 228*8ccd4a63SDavid du Colombier c3 = c2-NSIGNIF; 229*8ccd4a63SDavid du Colombier c2 = NSIGNIF; 230*8ccd4a63SDavid du Colombier } 231*8ccd4a63SDavid du Colombier 232*8ccd4a63SDavid du Colombier /* 233*8ccd4a63SDavid du Colombier * copy digits 234*8ccd4a63SDavid du Colombier */ 235*8ccd4a63SDavid du Colombier while(c1 > 0) { 236*8ccd4a63SDavid du Colombier if(c1+c2+c3 == c4) 237*8ccd4a63SDavid du Colombier s2[d++] = '.'; 238*8ccd4a63SDavid du Colombier s2[d++] = '0'; 239*8ccd4a63SDavid du Colombier c1--; 240*8ccd4a63SDavid du Colombier } 241*8ccd4a63SDavid du Colombier while(c2 > 0) { 242*8ccd4a63SDavid du Colombier if(c2+c3 == c4) 243*8ccd4a63SDavid du Colombier s2[d++] = '.'; 244*8ccd4a63SDavid du Colombier s2[d++] = s1[i++]; 245*8ccd4a63SDavid du Colombier c2--; 246*8ccd4a63SDavid du Colombier } 247*8ccd4a63SDavid du Colombier while(c3 > 0) { 248*8ccd4a63SDavid du Colombier if(c3 == c4) 249*8ccd4a63SDavid du Colombier s2[d++] = '.'; 250*8ccd4a63SDavid du Colombier s2[d++] = '0'; 251*8ccd4a63SDavid du Colombier c3--; 252*8ccd4a63SDavid du Colombier } 253*8ccd4a63SDavid du Colombier 254*8ccd4a63SDavid du Colombier /* 255*8ccd4a63SDavid du Colombier * strip trailing '0' on g conv 256*8ccd4a63SDavid du Colombier */ 257*8ccd4a63SDavid du Colombier if(fmt->flags & FmtSharp) { 258*8ccd4a63SDavid du Colombier if(0 == c4) 259*8ccd4a63SDavid du Colombier s2[d++] = '.'; 260*8ccd4a63SDavid du Colombier } else 261*8ccd4a63SDavid du Colombier if(chr == 'g' || chr == 'h') { 262*8ccd4a63SDavid du Colombier for(n=d-1; n>=0; n--) 263*8ccd4a63SDavid du Colombier if(s2[n] != '0') 264*8ccd4a63SDavid du Colombier break; 265*8ccd4a63SDavid du Colombier for(i=n; i>=0; i--) 266*8ccd4a63SDavid du Colombier if(s2[i] == '.') { 267*8ccd4a63SDavid du Colombier d = n; 268*8ccd4a63SDavid du Colombier if(i != n) 269*8ccd4a63SDavid du Colombier d++; 270*8ccd4a63SDavid du Colombier break; 271*8ccd4a63SDavid du Colombier } 272*8ccd4a63SDavid du Colombier } 273*8ccd4a63SDavid du Colombier if(chr == 'e' || chr == 'g') { 274*8ccd4a63SDavid du Colombier if(ucase) 275*8ccd4a63SDavid du Colombier s2[d++] = 'E'; 276*8ccd4a63SDavid du Colombier else 277*8ccd4a63SDavid du Colombier s2[d++] = 'e'; 278*8ccd4a63SDavid du Colombier c1 = e; 279*8ccd4a63SDavid du Colombier if(c1 < 0) { 280*8ccd4a63SDavid du Colombier s2[d++] = '-'; 281*8ccd4a63SDavid du Colombier c1 = -c1; 282*8ccd4a63SDavid du Colombier } else 283*8ccd4a63SDavid du Colombier s2[d++] = '+'; 284*8ccd4a63SDavid du Colombier if(c1 >= 100) { 285*8ccd4a63SDavid du Colombier s2[d++] = c1/100 + '0'; 286*8ccd4a63SDavid du Colombier c1 = c1%100; 287*8ccd4a63SDavid du Colombier } 288*8ccd4a63SDavid du Colombier s2[d++] = c1/10 + '0'; 289*8ccd4a63SDavid du Colombier s2[d++] = c1%10 + '0'; 290*8ccd4a63SDavid du Colombier } 291*8ccd4a63SDavid du Colombier s2[d] = 0; 292*8ccd4a63SDavid du Colombier } 293*8ccd4a63SDavid du Colombier 294*8ccd4a63SDavid du Colombier int 295*8ccd4a63SDavid du Colombier _floatfmt(Fmt *fmt, double f) 296*8ccd4a63SDavid du Colombier { 297*8ccd4a63SDavid du Colombier char s[FDIGIT+10]; 298*8ccd4a63SDavid du Colombier 299*8ccd4a63SDavid du Colombier xdtoa(fmt, s, f); 300*8ccd4a63SDavid du Colombier fmt->flags &= FmtWidth|FmtLeft; 301*8ccd4a63SDavid du Colombier _fmtcpy(fmt, s, strlen(s), strlen(s)); 302*8ccd4a63SDavid du Colombier return 0; 303*8ccd4a63SDavid du Colombier } 304*8ccd4a63SDavid du Colombier 305*8ccd4a63SDavid du Colombier int 306*8ccd4a63SDavid du Colombier _efgfmt(Fmt *f) 307*8ccd4a63SDavid du Colombier { 308*8ccd4a63SDavid du Colombier double d; 309*8ccd4a63SDavid du Colombier 310*8ccd4a63SDavid du Colombier d = va_arg(f->args, double); 311*8ccd4a63SDavid du Colombier return _floatfmt(f, d); 312*8ccd4a63SDavid du Colombier } 313