|
| 1 | +/* The authors of this software are Rob Pike and Ken Thompson. |
| 2 | + * Copyright (c) 2002 by Lucent Technologies. |
| 3 | + * Permission to use, copy, modify, and distribute this software for any |
| 4 | + * purpose without fee is hereby granted, provided that this entire notice |
| 5 | + * is included in all copies of any software which is or includes a copy |
| 6 | + * or modification of this software and in all copies of the supporting |
| 7 | + * documentation for such software. |
| 8 | + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED |
| 9 | + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY |
| 10 | + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY |
| 11 | + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. |
| 12 | + */ |
| 13 | + |
| 14 | +#include <stdio.h> |
| 15 | +#include <math.h> |
| 16 | +#include <float.h> |
| 17 | +#include <string.h> |
| 18 | +#include <stdlib.h> |
| 19 | +#include <errno.h> |
| 20 | + |
| 21 | +enum { NSIGNIF = 17 }; |
| 22 | + |
| 23 | +/* |
| 24 | + * first few powers of 10, enough for about 1/2 of the |
| 25 | + * total space for doubles. |
| 26 | + */ |
| 27 | +static double pows10[] = |
| 28 | +{ |
| 29 | + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, |
| 30 | + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, |
| 31 | + 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, |
| 32 | + 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, |
| 33 | + 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, |
| 34 | + 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, |
| 35 | + 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, |
| 36 | + 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, |
| 37 | + 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, |
| 38 | + 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, |
| 39 | + 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, |
| 40 | + 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, |
| 41 | + 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, |
| 42 | + 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, |
| 43 | + 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, |
| 44 | + 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, |
| 45 | +}; |
| 46 | +#define npows10 ((int)(sizeof(pows10)/sizeof(pows10[0]))) |
| 47 | +#define pow10(x) fmtpow10(x) |
| 48 | + |
| 49 | +static double |
| 50 | +pow10(int n) |
| 51 | +{ |
| 52 | + double d; |
| 53 | + int neg; |
| 54 | + |
| 55 | + neg = 0; |
| 56 | + if(n < 0){ |
| 57 | + neg = 1; |
| 58 | + n = -n; |
| 59 | + } |
| 60 | + |
| 61 | + if(n < npows10) |
| 62 | + d = pows10[n]; |
| 63 | + else{ |
| 64 | + d = pows10[npows10-1]; |
| 65 | + for(;;){ |
| 66 | + n -= npows10 - 1; |
| 67 | + if(n < npows10){ |
| 68 | + d *= pows10[n]; |
| 69 | + break; |
| 70 | + } |
| 71 | + d *= pows10[npows10 - 1]; |
| 72 | + } |
| 73 | + } |
| 74 | + if(neg) |
| 75 | + return 1./d; |
| 76 | + return d; |
| 77 | +} |
| 78 | + |
| 79 | +/* |
| 80 | + * add 1 to the decimal integer string a of length n. |
| 81 | + * if 99999 overflows into 10000, return 1 to tell caller |
| 82 | + * to move the virtual decimal point. |
| 83 | + */ |
| 84 | +static int |
| 85 | +xadd1(char *a, int n) |
| 86 | +{ |
| 87 | + char *b; |
| 88 | + int c; |
| 89 | + |
| 90 | + if(n < 0 || n > NSIGNIF) |
| 91 | + return 0; |
| 92 | + for(b = a+n-1; b >= a; b--) { |
| 93 | + c = *b + 1; |
| 94 | + if(c <= '9') { |
| 95 | + *b = c; |
| 96 | + return 0; |
| 97 | + } |
| 98 | + *b = '0'; |
| 99 | + } |
| 100 | + /* |
| 101 | + * need to overflow adding digit. |
| 102 | + * shift number down and insert 1 at beginning. |
| 103 | + * decimal is known to be 0s or we wouldn't |
| 104 | + * have gotten this far. (e.g., 99999+1 => 00000) |
| 105 | + */ |
| 106 | + a[0] = '1'; |
| 107 | + return 1; |
| 108 | +} |
| 109 | + |
| 110 | +/* |
| 111 | + * subtract 1 from the decimal integer string a. |
| 112 | + * if 10000 underflows into 09999, make it 99999 |
| 113 | + * and return 1 to tell caller to move the virtual |
| 114 | + * decimal point. this way, xsub1 is inverse of xadd1. |
| 115 | + */ |
| 116 | +static int |
| 117 | +xsub1(char *a, int n) |
| 118 | +{ |
| 119 | + char *b; |
| 120 | + int c; |
| 121 | + |
| 122 | + if(n < 0 || n > NSIGNIF) |
| 123 | + return 0; |
| 124 | + for(b = a+n-1; b >= a; b--) { |
| 125 | + c = *b - 1; |
| 126 | + if(c >= '0') { |
| 127 | + if(c == '0' && b == a) { |
| 128 | + /* |
| 129 | + * just zeroed the top digit; shift everyone up. |
| 130 | + * decimal is known to be 9s or we wouldn't |
| 131 | + * have gotten this far. (e.g., 10000-1 => 09999) |
| 132 | + */ |
| 133 | + *b = '9'; |
| 134 | + return 1; |
| 135 | + } |
| 136 | + *b = c; |
| 137 | + return 0; |
| 138 | + } |
| 139 | + *b = '9'; |
| 140 | + } |
| 141 | + /* |
| 142 | + * can't get here. the number a is always normalized |
| 143 | + * so that it has a nonzero first digit. |
| 144 | + */ |
| 145 | + abort(); |
| 146 | +} |
| 147 | + |
| 148 | +/* |
| 149 | + * format exponent like sprintf(p, "e%+d", e) |
| 150 | + */ |
| 151 | +void |
| 152 | +jsV_fmtexp(char *p, int e) |
| 153 | +{ |
| 154 | + char se[9]; |
| 155 | + int i; |
| 156 | + |
| 157 | + *p++ = 'e'; |
| 158 | + if(e < 0) { |
| 159 | + *p++ = '-'; |
| 160 | + e = -e; |
| 161 | + } else |
| 162 | + *p++ = '+'; |
| 163 | + i = 0; |
| 164 | + while(e) { |
| 165 | + se[i++] = e % 10 + '0'; |
| 166 | + e /= 10; |
| 167 | + } |
| 168 | + while(i < 1) |
| 169 | + se[i++] = '0'; |
| 170 | + while(i > 0) |
| 171 | + *p++ = se[--i]; |
| 172 | + *p++ = '\0'; |
| 173 | +} |
| 174 | + |
| 175 | +/* |
| 176 | + * compute decimal integer m, exp such that: |
| 177 | + * f = m*10^exp |
| 178 | + * m is as short as possible with losing exactness |
| 179 | + * assumes special cases (NaN, +Inf, -Inf) have been handled. |
| 180 | + */ |
| 181 | +void |
| 182 | +jsV_dtoa(double f, char *s, int *exp, int *neg, int *ns) |
| 183 | +{ |
| 184 | + int c, d, e2, e, ee, i, ndigit, oerrno; |
| 185 | + char tmp[NSIGNIF+10]; |
| 186 | + double g; |
| 187 | + |
| 188 | + oerrno = errno; /* in case strtod smashes errno */ |
| 189 | + |
| 190 | + /* |
| 191 | + * make f non-negative. |
| 192 | + */ |
| 193 | + *neg = 0; |
| 194 | + if(f < 0) { |
| 195 | + f = -f; |
| 196 | + *neg = 1; |
| 197 | + } |
| 198 | + |
| 199 | + /* |
| 200 | + * must handle zero specially. |
| 201 | + */ |
| 202 | + if(f == 0){ |
| 203 | + *exp = 0; |
| 204 | + s[0] = '0'; |
| 205 | + s[1] = '\0'; |
| 206 | + *ns = 1; |
| 207 | + return; |
| 208 | + } |
| 209 | + |
| 210 | + /* |
| 211 | + * find g,e such that f = g*10^e. |
| 212 | + * guess 10-exponent using 2-exponent, then fine tune. |
| 213 | + */ |
| 214 | + frexp(f, &e2); |
| 215 | + e = (int)(e2 * .301029995664); |
| 216 | + g = f * pow10(-e); |
| 217 | + while(g < 1) { |
| 218 | + e--; |
| 219 | + g = f * pow10(-e); |
| 220 | + } |
| 221 | + while(g >= 10) { |
| 222 | + e++; |
| 223 | + g = f * pow10(-e); |
| 224 | + } |
| 225 | + |
| 226 | + /* |
| 227 | + * convert NSIGNIF digits as a first approximation. |
| 228 | + */ |
| 229 | + for(i=0; i<NSIGNIF; i++) { |
| 230 | + d = (int)g; |
| 231 | + s[i] = d+'0'; |
| 232 | + g = (g-d) * 10; |
| 233 | + } |
| 234 | + s[i] = 0; |
| 235 | + |
| 236 | + /* |
| 237 | + * adjust e because s is 314159... not 3.14159... |
| 238 | + */ |
| 239 | + e -= NSIGNIF-1; |
| 240 | + jsV_fmtexp(s+NSIGNIF, e); |
| 241 | + |
| 242 | + /* |
| 243 | + * adjust conversion until strtod(s) == f exactly. |
| 244 | + */ |
| 245 | + for(i=0; i<10; i++) { |
| 246 | + g = strtod(s, NULL); |
| 247 | + if(f > g) { |
| 248 | + if(xadd1(s, NSIGNIF)) { |
| 249 | + /* gained a digit */ |
| 250 | + e--; |
| 251 | + jsV_fmtexp(s+NSIGNIF, e); |
| 252 | + } |
| 253 | + continue; |
| 254 | + } |
| 255 | + if(f < g) { |
| 256 | + if(xsub1(s, NSIGNIF)) { |
| 257 | + /* lost a digit */ |
| 258 | + e++; |
| 259 | + jsV_fmtexp(s+NSIGNIF, e); |
| 260 | + } |
| 261 | + continue; |
| 262 | + } |
| 263 | + break; |
| 264 | + } |
| 265 | + |
| 266 | + /* |
| 267 | + * play with the decimal to try to simplify. |
| 268 | + */ |
| 269 | + |
| 270 | + /* |
| 271 | + * bump last few digits up to 9 if we can |
| 272 | + */ |
| 273 | + for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) { |
| 274 | + c = s[i]; |
| 275 | + if(c != '9') { |
| 276 | + s[i] = '9'; |
| 277 | + g = strtod(s, NULL); |
| 278 | + if(g != f) { |
| 279 | + s[i] = c; |
| 280 | + break; |
| 281 | + } |
| 282 | + } |
| 283 | + } |
| 284 | + |
| 285 | + /* |
| 286 | + * add 1 in hopes of turning 9s to 0s |
| 287 | + */ |
| 288 | + if(s[NSIGNIF-1] == '9') { |
| 289 | + strcpy(tmp, s); |
| 290 | + ee = e; |
| 291 | + if(xadd1(tmp, NSIGNIF)) { |
| 292 | + ee--; |
| 293 | + jsV_fmtexp(tmp+NSIGNIF, ee); |
| 294 | + } |
| 295 | + g = strtod(tmp, NULL); |
| 296 | + if(g == f) { |
| 297 | + strcpy(s, tmp); |
| 298 | + e = ee; |
| 299 | + } |
| 300 | + } |
| 301 | + |
| 302 | + /* |
| 303 | + * bump last few digits down to 0 as we can. |
| 304 | + */ |
| 305 | + for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) { |
| 306 | + c = s[i]; |
| 307 | + if(c != '0') { |
| 308 | + s[i] = '0'; |
| 309 | + g = strtod(s, NULL); |
| 310 | + if(g != f) { |
| 311 | + s[i] = c; |
| 312 | + break; |
| 313 | + } |
| 314 | + } |
| 315 | + } |
| 316 | + |
| 317 | + /* |
| 318 | + * remove trailing zeros. |
| 319 | + */ |
| 320 | + ndigit = NSIGNIF; |
| 321 | + while(ndigit > 1 && s[ndigit-1] == '0'){ |
| 322 | + e++; |
| 323 | + --ndigit; |
| 324 | + } |
| 325 | + s[ndigit] = 0; |
| 326 | + *exp = e; |
| 327 | + *ns = ndigit; |
| 328 | + errno = oerrno; |
| 329 | +} |
0 commit comments