diff options
author | Rob Pike <r@golang.org> | 2008-06-04 14:46:07 -0700 |
---|---|---|
committer | Rob Pike <r@golang.org> | 2008-06-04 14:46:07 -0700 |
commit | 5cf1e37b96825023b2da8de9be3a07d1987bb306 (patch) | |
tree | fda9e615c7d6ddb748cee567027fae05ba9215ae /src/old/c/mpatof.c | |
parent | 0a2697043d69739b27b6ba872b2092a4c2bb61a8 (diff) | |
download | golang-5cf1e37b96825023b2da8de9be3a07d1987bb306.tar.gz |
move old code into 'old' directory
add src/test dir
SVN=121166
Diffstat (limited to 'src/old/c/mpatof.c')
-rw-r--r-- | src/old/c/mpatof.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/src/old/c/mpatof.c b/src/old/c/mpatof.c new file mode 100644 index 000000000..07bcf4a27 --- /dev/null +++ b/src/old/c/mpatof.c @@ -0,0 +1,342 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <u.h> +#include <libc.h> + +int mpatof(char*, double*); +int mpatov(char *s, vlong *v); + +enum +{ + Mpscale = 29, /* safely smaller than bits in a long */ + Mpprec = 36, /* Mpscale*Mpprec sb > largest fp exp */ + Mpbase = 1L<<Mpscale, +}; + +typedef +struct +{ + long a[Mpprec]; + char ovf; +} Mp; + +static void mpint(Mp*, int); +static void mppow(Mp*, int, int); +static void mpmul(Mp*, int); +static void mpadd(Mp*, Mp*); +static int mptof(Mp*, double*); + +/* + * convert a string, s, to floating in *d + * return conversion overflow. + * required syntax is [+-]d*[.]d*[e[+-]d*] + */ +int +mpatof(char *s, double *d) +{ + Mp a, b; + int dp, c, f, ef, ex, zer; + double d1, d2; + + dp = 0; /* digits after decimal point */ + f = 0; /* sign */ + ex = 0; /* exponent */ + zer = 1; /* zero */ + memset(&a, 0, sizeof(a)); + for(;;) { + switch(c = *s++) { + default: + goto bad; + case '-': + f = 1; + case ' ': + case '\t': + case '+': + continue; + case '.': + dp = 1; + continue; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + zer = 0; + case '0': + mpint(&b, c-'0'); + mpmul(&a, 10); + mpadd(&a, &b); + if(dp) + dp++; + continue; + case 'E': + case 'e': + ex = 0; + ef = 0; + for(;;) { + c = *s++; + if(c == '+' || c == ' ' || c == '\t') + continue; + if(c == '-') { + ef = 1; + continue; + } + if(c >= '0' && c <= '9') { + ex = ex*10 + (c-'0'); + continue; + } + break; + } + if(ef) + ex = -ex; + case 0: + break; + } + break; + } + if(a.ovf) + goto bad; + if(zer) { + *d = 0; + return 0; + } + if(dp) + dp--; + dp -= ex; + if(dp > 0) { + /* + * must divide by 10**dp + */ + if(mptof(&a, &d1)) + goto bad; + + /* + * trial exponent of 8**dp + * 8 (being between 5 and 10) + * should pick up all underflows + * in the division of 5**dp. + */ + d2 = frexp(d1, &ex); + d2 = ldexp(d2, ex-3*dp); + if(d2 == 0) + goto bad; + + /* + * decompose each 10 into 5*2. + * create 5**dp in fixed point + * and then play with the exponent + * for the remaining 2**dp. + * note that 5**dp will overflow + * with as few as 134 input digits. + */ + mpint(&a, 1); + mppow(&a, 5, dp); + if(mptof(&a, &d2)) + goto bad; + d1 = frexp(d1/d2, &ex); + d1 = ldexp(d1, ex-dp); + if(d1 == 0) + goto bad; + } else { + /* + * must multiply by 10**|dp| -- + * just do it in fixed point. + */ + mppow(&a, 10, -dp); + if(mptof(&a, &d1)) + goto bad; + } + if(f) + d1 = -d1; + *d = d1; + return 0; + +bad: + return 1; +} + +/* + * convert a to floating in *d + * return conversion overflow + */ +static int +mptof(Mp *a, double *d) +{ + double f, g; + long x, *a1; + int i; + + if(a->ovf) + return 1; + a1 = a->a; + f = ldexp(*a1++, 0); + for(i=Mpscale; i<Mpprec*Mpscale; i+=Mpscale) + if(x = *a1++) { + g = ldexp(x, i); + /* + * NOTE: the test (g==0) is plan9 + * specific. ansi compliant overflow + * is signaled by HUGE and errno==ERANGE. + * change this for your particular ldexp. + */ + if(g == 0) + return 1; + f += g; /* this could bomb! */ + } + *d = f; + return 0; +} + +/* + * return a += b + */ +static void +mpadd(Mp *a, Mp *b) +{ + int i, c; + long x, *a1, *b1; + + if(b->ovf) + a->ovf = 1; + if(a->ovf) + return; + c = 0; + a1 = a->a; + b1 = b->a; + for(i=0; i<Mpprec; i++) { + x = *a1 + *b1++ + c; + c = 0; + if(x >= Mpbase) { + x -= Mpbase; + c = 1; + } + *a1++ = x; + } + a->ovf = c; +} + +/* + * return a = c + */ +static void +mpint(Mp *a, int c) +{ + + memset(a, 0, sizeof(*a)); + a->a[0] = c; +} + +/* + * return a *= c + */ +static void +mpmul(Mp *a, int c) +{ + Mp p; + int b; + memmove(&p, a, sizeof(p)); + if(!(c & 1)) + memset(a, 0, sizeof(*a)); + c &= ~1; + for(b=2; c; b<<=1) { + mpadd(&p, &p); + if(c & b) { + mpadd(a, &p); + c &= ~b; + } + } +} + +/* + * return a *= b**e + */ +static void +mppow(Mp *a, int b, int e) +{ + int b1; + + b1 = b*b; + b1 = b1*b1; + while(e >= 4) { + mpmul(a, b1); + e -= 4; + if(a->ovf) + return; + } + while(e > 0) { + mpmul(a, b); + e--; + } +} + +/* + * convert a string, s, to vlong in *v + * return conversion overflow. + * required syntax is [0[x]]d* + */ +int +mpatov(char *s, vlong *v) +{ + vlong n, nn; + int c; + n = 0; + c = *s; + if(c == '0') + goto oct; + while(c = *s++) { + if(c >= '0' && c <= '9') + nn = n*10 + c-'0'; + else + goto bad; + if(n < 0 && nn >= 0) + goto bad; + n = nn; + } + goto out; +oct: + s++; + c = *s; + if(c == 'x' || c == 'X') + goto hex; + while(c = *s++) { + if(c >= '0' || c <= '7') + nn = n*8 + c-'0'; + else + goto bad; + if(n < 0 && nn >= 0) + goto bad; + n = nn; + } + goto out; +hex: + s++; + while(c = *s++) { + if(c >= '0' && c <= '9') + c += 0-'0'; + else + if(c >= 'a' && c <= 'f') + c += 10-'a'; + else + if(c >= 'A' && c <= 'F') + c += 10-'A'; + else + goto bad; + nn = n*16 + c; + if(n < 0 && nn >= 0) + goto bad; + n = nn; + } +out: + *v = n; + return 0; + +bad: + *v = ~0; + return 1; +} |