| // 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; | |
| } |