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