root/src/cmd/gc/mparith1.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mpcmpfixflt
  2. mpcmpfltfix
  3. mpcmpfixfix
  4. mpcmpfixc
  5. mpcmpfltflt
  6. mpcmpfltc
  7. mpsubfixfix
  8. mpsubfltflt
  9. mpaddcfix
  10. mpaddcflt
  11. mpmulcfix
  12. mpmulcflt
  13. mpdivfixfix
  14. mpmodfixfix
  15. mpcomfix
  16. mpmovefixflt
  17. mpexactfltfix
  18. mpmovefltfix
  19. mpmovefixfix
  20. mpmovefltflt
  21. mppow10flt
  22. mphextofix
  23. mpatoflt
  24. mpatofix
  25. Bconv
  26. Fconv

// 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>
#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 c1;

        mpmovecfix(&c1, c);
        return mpcmpfixfix(b, &c1);
}

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(b, &a);
}

void
mpsubfixfix(Mpint *a, Mpint *b)
{
        mpnegfix(a);
        mpaddfixfix(a, b, 0);
        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, 0);
}

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

static void
mphextofix(Mpint *a, char *s, int n)
{
        char *hexdigitp, *end, c;
        long d;
        int bit;

        while(*s == '0') {
                s++;
                n--;
        }

        // overflow
        if(4*n > Mpscale*Mpprec) {
                a->ovf = 1;
                return;
        }

        end = s+n-1;
        for(hexdigitp=end; hexdigitp>=s; hexdigitp--) {
                c = *hexdigitp;
                if(c >= '0' && c <= '9')
                        d = c-'0';
                else if(c >= 'A' && c <= 'F')
                        d = c-'A'+10;
                else
                        d = c-'a'+10;

                bit = 4*(end - hexdigitp);
                while(d > 0) {
                        if(d & 1)
                                a->a[bit/Mpscale] |= (long)1 << (bit%Mpscale);
                        bit++;
                        d = d >> 1;
                }
        }
}

//
// floating point input
// required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*]
//
void
mpatoflt(Mpflt *a, char *as)
{
        Mpflt b;
        int dp, c, f, ef, ex, eb, base;
        char *s, *start;

        while(*as == ' ' || *as == '\t')
                as++;

        /* determine base */
        s = as;
        base = -1;
        while(base == -1) {
                switch(*s++) {
                case '-':
                case '+':
                        break;

                case '0':
                        if(*s == 'x')
                                base = 16;
                        else
                                base = 10;
                        break;

                default:
                        base = 10;
                }
        }

        s = as;
        dp = 0;         /* digits after decimal point */
        f = 0;          /* sign */
        ex = 0;         /* exponent */
        eb = 0;         /* binary point */

        mpmovecflt(a, 0.0);
        if(base == 16) {
                start = nil;
                for(;;) {
                        c = *s;
                        if(c == '-') {
                                f = 1;
                                s++;
                        }
                        else if(c == '+') {
                                s++;
                        }
                        else if(c == '0' && s[1] == 'x') {
                                s += 2;
                                start = s;
                        }
                        else if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
                                s++;
                        }
                        else {
                                break;
                        }
                }
                if(start == nil)
                        goto bad;

                mphextofix(&a->val, start, s-start);
                if(a->val.ovf)
                        goto bad;
                a->exp = 0;
                mpnorm(a);
        }
        for(;;) {
                switch(c = *s++) {
                default:
                        goto bad;

                case '-':
                        f = 1;

                case ' ':
                case '\t':
                case '+':
                        continue;

                case '.':
                        if(base == 16)
                                goto bad;
                        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("constant exponent out of range: %s", as);
                                                errorexit();
                                        }
                                        continue;
                                }
                                break;
                        }
                        if(ef)
                                ex = -ex;

                case 0:
                        break;
                }
                break;
        }

        if(eb) {
                if(dp)
                        goto bad;
                mpsetexp(a, 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 {
                        // 4 approximates least_upper_bound(log2(10)).
                        if(dp-ex >= (1<<(8*sizeof(dp)-3)) || (short)(4*(dp-ex)) != 4*(dp-ex)) {
                                mpmovecflt(a, 0.0);
                        }
                        else {
                                mppow10flt(&b, dp-ex);
                                mpdivfltflt(a, &b);
                        }
                }
        }

out:
        if(f)
                mpnegflt(a);
        return;

bad:
        yyerror("constant too large: %s", as);
        mpmovecflt(a, 0.0);
}

//
// fixed point input
// required syntax is [+-][0[x]]d*
//
void
mpatofix(Mpint *a, char *as)
{
        int c, f;
        char *s, *s0;

        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:
        s0 = s;
        c = *s;
        while(c) {
                if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
                        s++;
                        c = *s;
                        continue;
                }
                goto bad;
        }
        mphextofix(a, s0, s-s0);
        if(a->ovf)
                goto bad;

out:
        if(f)
                mpnegfix(a);
        return;

bad:
        yyerror("constant too large: %s", as);
        mpmovecfix(a, 0);
}

int
Bconv(Fmt *fp)
{
        char buf[500], *p;
        Mpint *xval, q, r, ten, sixteen;
        int f, digit;

        xval = va_arg(fp->args, Mpint*);
        mpmovefixfix(&q, xval);
        f = 0;
        if(mptestfix(&q) < 0) {
                f = 1;
                mpnegfix(&q);
        }

        p = &buf[sizeof(buf)];
        *--p = 0;
        if(fp->flags & FmtSharp) {
                // Hexadecimal
                mpmovecfix(&sixteen, 16);
                for(;;) {
                        mpdivmodfixfix(&q, &r, &q, &sixteen);
                        digit = mpgetfix(&r);
                        if(digit < 10)
                                *--p = digit + '0';
                        else
                                *--p = digit - 10 + 'A';
                        if(mptestfix(&q) <= 0)
                                break;
                }
                *--p = 'x';
                *--p = '0';
        } else {
                // Decimal
                mpmovecfix(&ten, 10);
                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, dexp;
        int exp;

        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
                exp = fvp->exp + sigfig(fvp)*Mpscale;
                if(-900 < exp && exp < 900) {
                        d = mpgetflt(fvp);
                        if(d >= 0 && (fp->flags & FmtSign))
                                fmtprint(fp, "+");
                        return fmtprint(fp, "%g", d, exp, fvp);
                }
                
                // very out of range. compute decimal approximation by hand.
                // decimal exponent
                dexp = fvp->exp * 0.301029995663981195; // log_10(2)
                exp = (int)dexp;
                // decimal mantissa
                fv = *fvp;
                fv.val.neg = 0;
                fv.exp = 0;
                d = mpgetflt(&fv);
                d *= pow(10, dexp-exp);
                while(d >= 9.99995) {
                        d /= 10;
                        exp++;
                }
                if(fvp->val.neg)
                        fmtprint(fp, "-");
                else if(fp->flags & FmtSign)
                        fmtprint(fp, "+");
                return fmtprint(fp, "%.5fe+%d", d, exp);
        }

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

/* [<][>][^][v][top][bottom][index][help] */