| // 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 "go.h" |
| |
| /// uses arithmetic |
| |
| int |
| mpcmpfixflt(Mpint *a, Mpflt *b) |
| { |
| char buf[500]; |
| Mpflt c; |
| |
| snprint(buf, sizeof(buf), "%B", a); |
| mpatoflt(&c, buf); |
| return mpcmpfltflt(&c, b); |
| } |
| |
| int |
| mpcmpfltfix(Mpflt *a, Mpint *b) |
| { |
| char buf[500]; |
| Mpflt c; |
| |
| snprint(buf, sizeof(buf), "%B", b); |
| mpatoflt(&c, buf); |
| return mpcmpfltflt(a, &c); |
| } |
| |
| int |
| mpcmpfixfix(Mpint *a, Mpint *b) |
| { |
| Mpint c; |
| |
| mpmovefixfix(&c, a); |
| mpsubfixfix(&c, b); |
| return mptestfix(&c); |
| } |
| |
| int |
| mpcmpfixc(Mpint *b, vlong c) |
| { |
| Mpint a; |
| |
| mpmovecfix(&a, c); |
| return mpcmpfixfix(&a, b); |
| } |
| |
| int |
| mpcmpfltflt(Mpflt *a, Mpflt *b) |
| { |
| Mpflt c; |
| |
| mpmovefltflt(&c, a); |
| mpsubfltflt(&c, b); |
| return mptestflt(&c); |
| } |
| |
| int |
| mpcmpfltc(Mpflt *b, double c) |
| { |
| Mpflt a; |
| |
| mpmovecflt(&a, c); |
| return mpcmpfltflt(&a, b); |
| } |
| |
| void |
| mpsubfixfix(Mpint *a, Mpint *b) |
| { |
| mpnegfix(a); |
| mpaddfixfix(a, b); |
| mpnegfix(a); |
| } |
| |
| void |
| mpsubfltflt(Mpflt *a, Mpflt *b) |
| { |
| mpnegflt(a); |
| mpaddfltflt(a, b); |
| mpnegflt(a); |
| } |
| |
| void |
| mpaddcfix(Mpint *a, vlong c) |
| { |
| Mpint b; |
| |
| mpmovecfix(&b, c); |
| mpaddfixfix(a, &b); |
| } |
| |
| void |
| mpaddcflt(Mpflt *a, double c) |
| { |
| Mpflt b; |
| |
| mpmovecflt(&b, c); |
| mpaddfltflt(a, &b); |
| } |
| |
| void |
| mpmulcfix(Mpint *a, vlong c) |
| { |
| Mpint b; |
| |
| mpmovecfix(&b, c); |
| mpmulfixfix(a, &b); |
| } |
| |
| void |
| mpmulcflt(Mpflt *a, double c) |
| { |
| Mpflt b; |
| |
| mpmovecflt(&b, c); |
| mpmulfltflt(a, &b); |
| } |
| |
| void |
| mpdivfixfix(Mpint *a, Mpint *b) |
| { |
| Mpint q, r; |
| |
| mpdivmodfixfix(&q, &r, a, b); |
| mpmovefixfix(a, &q); |
| } |
| |
| void |
| mpmodfixfix(Mpint *a, Mpint *b) |
| { |
| Mpint q, r; |
| |
| mpdivmodfixfix(&q, &r, a, b); |
| mpmovefixfix(a, &r); |
| } |
| |
| void |
| mpcomfix(Mpint *a) |
| { |
| Mpint b; |
| |
| mpmovecfix(&b, 1); |
| mpnegfix(a); |
| mpsubfixfix(a, &b); |
| } |
| |
| void |
| mpmovefixflt(Mpflt *a, Mpint *b) |
| { |
| a->val = *b; |
| a->exp = 0; |
| mpnorm(a); |
| } |
| |
| // convert (truncate) b to a. |
| // return -1 (but still convert) if b was non-integer. |
| static int |
| mpexactfltfix(Mpint *a, Mpflt *b) |
| { |
| Mpflt f; |
| |
| *a = b->val; |
| mpshiftfix(a, b->exp); |
| if(b->exp < 0) { |
| f.val = *a; |
| f.exp = 0; |
| mpnorm(&f); |
| if(mpcmpfltflt(b, &f) != 0) |
| return -1; |
| } |
| return 0; |
| } |
| |
| int |
| mpmovefltfix(Mpint *a, Mpflt *b) |
| { |
| Mpflt f; |
| int i; |
| |
| if(mpexactfltfix(a, b) == 0) |
| return 0; |
| |
| // try rounding down a little |
| f = *b; |
| f.val.a[0] = 0; |
| if(mpexactfltfix(a, &f) == 0) |
| return 0; |
| |
| // try rounding up a little |
| for(i=1; i<Mpprec; i++) { |
| f.val.a[i]++; |
| if(f.val.a[i] != Mpbase) |
| break; |
| f.val.a[i] = 0; |
| } |
| mpnorm(&f); |
| if(mpexactfltfix(a, &f) == 0) |
| return 0; |
| |
| return -1; |
| } |
| |
| void |
| mpmovefixfix(Mpint *a, Mpint *b) |
| { |
| *a = *b; |
| } |
| |
| void |
| mpmovefltflt(Mpflt *a, Mpflt *b) |
| { |
| *a = *b; |
| } |
| |
| static double tab[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7 }; |
| static void |
| mppow10flt(Mpflt *a, int p) |
| { |
| if(p < 0) |
| abort(); |
| if(p < nelem(tab)) { |
| mpmovecflt(a, tab[p]); |
| return; |
| } |
| mppow10flt(a, p>>1); |
| mpmulfltflt(a, a); |
| if(p & 1) |
| mpmulcflt(a, 10); |
| } |
| |
| // |
| // floating point input |
| // required syntax is [+-]d*[.]d*[e[+-]d*] |
| // |
| void |
| mpatoflt(Mpflt *a, char *as) |
| { |
| Mpflt b; |
| int dp, c, f, ef, ex, eb; |
| char *s; |
| |
| s = as; |
| dp = 0; /* digits after decimal point */ |
| f = 0; /* sign */ |
| ex = 0; /* exponent */ |
| eb = 0; /* binary point */ |
| |
| mpmovecflt(a, 0.0); |
| 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': |
| case '0': |
| mpmulcflt(a, 10); |
| mpaddcflt(a, c-'0'); |
| if(dp) |
| dp++; |
| continue; |
| |
| case 'P': |
| case 'p': |
| eb = 1; |
| |
| 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'); |
| if(ex > 1e8) { |
| yyerror("exponent out of range"); |
| errorexit(); |
| } |
| continue; |
| } |
| break; |
| } |
| if(ef) |
| ex = -ex; |
| |
| case 0: |
| break; |
| } |
| break; |
| } |
| |
| if(eb) { |
| if(dp) |
| goto bad; |
| a->exp += ex; |
| goto out; |
| } |
| |
| if(dp) |
| dp--; |
| if(mpcmpfltc(a, 0.0) != 0) { |
| if(ex >= dp) { |
| mppow10flt(&b, ex-dp); |
| mpmulfltflt(a, &b); |
| } else { |
| mppow10flt(&b, dp-ex); |
| mpdivfltflt(a, &b); |
| } |
| } |
| |
| out: |
| if(f) |
| mpnegflt(a); |
| return; |
| |
| bad: |
| yyerror("set ovf in mpatof"); |
| mpmovecflt(a, 0.0); |
| } |
| |
| // |
| // fixed point input |
| // required syntax is [+-][0[x]]d* |
| // |
| void |
| mpatofix(Mpint *a, char *as) |
| { |
| int c, f; |
| char *s; |
| |
| s = as; |
| f = 0; |
| mpmovecfix(a, 0); |
| |
| c = *s++; |
| switch(c) { |
| case '-': |
| f = 1; |
| |
| case '+': |
| c = *s++; |
| if(c != '0') |
| break; |
| |
| case '0': |
| goto oct; |
| } |
| |
| while(c) { |
| if(c >= '0' && c <= '9') { |
| mpmulcfix(a, 10); |
| mpaddcfix(a, c-'0'); |
| c = *s++; |
| continue; |
| } |
| goto bad; |
| } |
| goto out; |
| |
| oct: |
| c = *s++; |
| if(c == 'x' || c == 'X') |
| goto hex; |
| while(c) { |
| if(c >= '0' && c <= '7') { |
| mpmulcfix(a, 8); |
| mpaddcfix(a, c-'0'); |
| c = *s++; |
| continue; |
| } |
| goto bad; |
| } |
| goto out; |
| |
| hex: |
| c = *s++; |
| while(c) { |
| if(c >= '0' && c <= '9') { |
| mpmulcfix(a, 16); |
| mpaddcfix(a, c-'0'); |
| c = *s++; |
| continue; |
| } |
| if(c >= 'a' && c <= 'f') { |
| mpmulcfix(a, 16); |
| mpaddcfix(a, c+10-'a'); |
| c = *s++; |
| continue; |
| } |
| if(c >= 'A' && c <= 'F') { |
| mpmulcfix(a, 16); |
| mpaddcfix(a, c+10-'A'); |
| c = *s++; |
| continue; |
| } |
| goto bad; |
| } |
| |
| out: |
| if(f) |
| mpnegfix(a); |
| return; |
| |
| bad: |
| yyerror("set ovf in mpatov: %s", as); |
| mpmovecfix(a, 0); |
| } |
| |
| int |
| Bconv(Fmt *fp) |
| { |
| char buf[500], *p; |
| Mpint *xval, q, r, ten; |
| int f; |
| |
| xval = va_arg(fp->args, Mpint*); |
| mpmovefixfix(&q, xval); |
| f = 0; |
| if(mptestfix(&q) < 0) { |
| f = 1; |
| mpnegfix(&q); |
| } |
| mpmovecfix(&ten, 10); |
| |
| p = &buf[sizeof(buf)]; |
| *--p = 0; |
| for(;;) { |
| mpdivmodfixfix(&q, &r, &q, &ten); |
| *--p = mpgetfix(&r) + '0'; |
| if(mptestfix(&q) <= 0) |
| break; |
| } |
| if(f) |
| *--p = '-'; |
| return fmtstrcpy(fp, p); |
| } |
| |
| int |
| Fconv(Fmt *fp) |
| { |
| char buf[500]; |
| Mpflt *fvp, fv; |
| double d; |
| |
| fvp = va_arg(fp->args, Mpflt*); |
| if(fp->flags & FmtSharp) { |
| // alternate form - decimal for error messages. |
| // for well in range, convert to double and use print's %g |
| if(-900 < fvp->exp && fvp->exp < 900) { |
| d = mpgetflt(fvp); |
| if(d >= 0 && (fp->flags & FmtSign)) |
| fmtprint(fp, "+"); |
| return fmtprint(fp, "%g", d); |
| } |
| // TODO(rsc): for well out of range, print |
| // an approximation like 1.234e1000 |
| } |
| |
| if(sigfig(fvp) == 0) { |
| snprint(buf, sizeof(buf), "0p+0"); |
| goto out; |
| } |
| fv = *fvp; |
| |
| while(fv.val.a[0] == 0) { |
| mpshiftfix(&fv.val, -Mpscale); |
| fv.exp += Mpscale; |
| } |
| while((fv.val.a[0]&1) == 0) { |
| mpshiftfix(&fv.val, -1); |
| fv.exp += 1; |
| } |
| |
| if(fv.exp >= 0) { |
| snprint(buf, sizeof(buf), "%Bp+%d", &fv.val, fv.exp); |
| goto out; |
| } |
| snprint(buf, sizeof(buf), "%Bp-%d", &fv.val, -fv.exp); |
| |
| out: |
| return fmtstrcpy(fp, buf); |
| } |