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