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