diff options
Diffstat (limited to 'mpfr.c')
-rw-r--r-- | mpfr.c | 246 |
1 files changed, 162 insertions, 84 deletions
@@ -2,22 +2,22 @@ * mpfr.c - routines for arbitrary-precision number support in gawk. */ -/* +/* * Copyright (C) 2012, 2013, 2015 the Free Software Foundation, Inc. - * + * * This file is part of GAWK, the GNU implementation of the * AWK Programming Language. - * + * * GAWK is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. - * + * * GAWK is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA @@ -118,7 +118,7 @@ mpg_node(unsigned int tp) mpz_init(r->mpg_i); r->flags = MPZN; } - + r->valref = 1; r->flags |= MALLOC|NUMBER|NUMCUR; r->stptr = NULL; @@ -151,7 +151,7 @@ mpg_make_number(double x) return r; } -/* mpg_strtoui --- assign arbitrary-precision integral value from a string */ +/* mpg_strtoui --- assign arbitrary-precision integral value from a string */ int mpg_strtoui(mpz_ptr zi, char *str, size_t len, char **end, int base) @@ -264,7 +264,7 @@ mpg_zero(NODE *n) n->flags &= ~MPFN; } if (! is_mpg_integer(n)) { - mpz_init(n->mpg_i); /* this also sets its value to 0 */ + mpz_init(n->mpg_i); /* this also sets its value to 0 */ n->flags |= MPZN; } else mpz_set_si(n->mpg_i, 0); @@ -329,13 +329,13 @@ force_mpnum(NODE *n, int do_nondec, int use_locale) IEEE_FMT(n->mpg_numbr, tval); done: /* trailing space is OK for NUMBER */ - while (isspace((unsigned char) *ptr)) + while (ptr < cpend && isspace((unsigned char) *ptr)) ptr++; *cpend = save; if (errno == 0 && ptr == cpend) return true; errno = 0; - return false; + return false; } /* mpg_force_number --- force a value to be a multiple-precision number */ @@ -343,20 +343,17 @@ done: static NODE * mpg_force_number(NODE *n) { - unsigned int newflags = 0; - - if (is_mpg_number(n) && (n->flags & NUMCUR) != 0) + if ((n->flags & NUMCUR) != 0) return n; - - if ((n->flags & MAYBE_NUM) != 0) { - n->flags &= ~MAYBE_NUM; - newflags = NUMBER; - } + n->flags |= NUMCUR; if (force_mpnum(n, (do_non_decimal_data && ! do_traditional), true)) { - n->flags |= newflags; - n->flags |= NUMCUR; - } + if ((n->flags & MAYBE_NUM) != 0) { + n->flags &= ~(MAYBE_NUM|STRING); + n->flags |= NUMBER; + } + } else + n->flags &= ~MAYBE_NUM; return n; } @@ -375,7 +372,7 @@ mpg_format_val(const char *format, int index, NODE *s) if (is_mpg_integer(s) || mpfr_integer_p(s->mpg_numbr)) { /* integral value, use %d */ r = format_tree("%d", 2, dummy, 2); - s->stfmt = -1; + s->stfmt = STFMT_UNUSED; } else { r = format_tree(format, fmt_list[index]->stlen, dummy, 2); assert(r != NULL); @@ -387,7 +384,7 @@ mpg_format_val(const char *format, int index, NODE *s) efree(s->stptr); s->stptr = r->stptr; freenode(r); /* Do not unref(r)! We want to keep s->stptr == r->stpr. */ - + s->flags |= STRCUR; free_wstr(s); return s; @@ -430,8 +427,8 @@ mpg_cmp(const NODE *t1, const NODE *t2) /* - * mpg_update_var --- update NR or FNR. - * NR_node->var_value(mpz_t) = MNR(mpz_t) * LONG_MAX + NR(long) + * mpg_update_var --- update NR or FNR. + * NR_node->var_value(mpz_t) = MNR(mpz_t) * LONG_MAX + NR(long) */ NODE * @@ -485,7 +482,7 @@ mpg_set_var(NODE *n) if (is_mpg_integer(val)) r = val->mpg_i; else { - /* convert float to integer */ + /* convert float to integer */ mpfr_get_z(mpzval, val->mpg_numbr, MPFR_RNDZ); r = mpzval; } @@ -523,11 +520,9 @@ set_PREC() if (! do_mpfr) return; - val = PREC_node->var_value; - if ((val->flags & MAYBE_NUM) != 0) - force_number(val); + val = fixtype(PREC_node->var_value); - if ((val->flags & STRCUR) != 0) { + if ((val->flags & STRING) != 0) { int i, j; /* emulate IEEE-754 binary format */ @@ -553,7 +548,7 @@ set_PREC() if (prec <= 0) { force_number(val); - prec = get_number_si(val); + prec = get_number_si(val); if (prec < MPFR_PREC_MIN || prec > MPFR_PREC_MAX) { force_string(val); warning(_("PREC value `%.*s' is invalid"), (int) val->stlen, val->stptr); @@ -677,9 +672,9 @@ do_mpfr_atan2(int nargs) t1 = POP_SCALAR(); if (do_lint) { - if ((t1->flags & (NUMCUR|NUMBER)) == 0) + if ((fixtype(t1)->flags & NUMBER) == 0) lintwarn(_("atan2: received non-numeric first argument")); - if ((t2->flags & (NUMCUR|NUMBER)) == 0) + if ((fixtype(t2)->flags & NUMBER) == 0) lintwarn(_("atan2: received non-numeric second argument")); } force_number(t1); @@ -688,7 +683,7 @@ do_mpfr_atan2(int nargs) p1 = MP_FLOAT(t1); p2 = MP_FLOAT(t2); res = mpg_float(); - /* See MPFR documentation for handling of special values like +inf as an argument */ + /* See MPFR documentation for handling of special values like +inf as an argument */ tval = mpfr_atan2(res->mpg_numbr, p1, p2, ROUND_MODE); IEEE_FMT(res->mpg_numbr, tval); @@ -710,7 +705,7 @@ do_mpfr_func(const char *name, mpfr_prec_t argprec; t1 = POP_SCALAR(); - if (do_lint && (t1->flags & (NUMCUR|NUMBER)) == 0) + if (do_lint && (fixtype(t1)->flags & NUMBER) == 0) lintwarn(_("%s: received non-numeric argument"), name); force_number(t1); @@ -777,7 +772,7 @@ do_mpfr_int(int nargs) NODE *tmp, *r; tmp = POP_SCALAR(); - if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) + if (do_lint && (fixtype(tmp)->flags & NUMBER) == 0) lintwarn(_("int: received non-numeric argument")); force_number(tmp); @@ -807,7 +802,7 @@ do_mpfr_compl(int nargs) mpz_ptr zptr; tmp = POP_SCALAR(); - if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) + if (do_lint && (fixtype(tmp)->flags & NUMBER) == 0) lintwarn(_("compl: received non-numeric argument")); force_number(tmp); @@ -818,28 +813,26 @@ do_mpfr_compl(int nargs) /* [+-]inf or NaN */ return tmp; } - if (do_lint) { - if (mpfr_sgn(p) < 0) - lintwarn("%s", - mpg_fmt(_("compl(%Rg): negative value will give strange results"), p) + if (mpfr_sgn(p) < 0) + fatal("%s", + mpg_fmt(_("compl(%Rg): negative value is not allowed"), p) ); + if (do_lint) { if (! mpfr_integer_p(p)) lintwarn("%s", mpg_fmt(_("comp(%Rg): fractional value will be truncated"), p) ); } - + mpfr_get_z(mpzval, p, MPFR_RNDZ); /* float to integer conversion */ zptr = mpzval; } else { - /* (tmp->flags & MPZN) != 0 */ + /* (tmp->flags & MPZN) != 0 */ zptr = tmp->mpg_i; - if (do_lint) { - if (mpz_sgn(zptr) < 0) - lintwarn("%s", - mpg_fmt(_("cmpl(%Zd): negative values will give strange results"), zptr) + if (mpz_sgn(zptr) < 0) + fatal("%s", + mpg_fmt(_("compl(%Zd): negative values is not allowed"), zptr) ); - } } r = mpg_integer(); @@ -855,7 +848,7 @@ get_intval(NODE *t1, int argnum, const char *op) { mpz_ptr pz; - if (do_lint && (t1->flags & (NUMCUR|NUMBER)) == 0) + if (do_lint && (fixtype(t1)->flags & NUMBER) == 0) lintwarn(_("%s: received non-numeric argument #%d"), op, argnum); (void) force_number(t1); @@ -875,13 +868,13 @@ get_intval(NODE *t1, int argnum, const char *op) return pz; /* should be freed */ } - if (do_lint) { - if (mpfr_sgn(left) < 0) - lintwarn("%s", - mpg_fmt(_("%s: argument #%d negative value %Rg will give strange results"), + if (mpfr_sgn(left) < 0) + fatal("%s", + mpg_fmt(_("%s: argument #%d negative value %Rg is not allowed"), op, argnum, left) ); + if (do_lint) { if (! mpfr_integer_p(left)) lintwarn("%s", mpg_fmt(_("%s: argument #%d fractional value %Rg will be truncated"), @@ -893,16 +886,15 @@ get_intval(NODE *t1, int argnum, const char *op) mpz_init(pz); mpfr_get_z(pz, left, MPFR_RNDZ); /* float to integer conversion */ return pz; /* should be freed */ - } - /* (t1->flags & MPZN) != 0 */ + } + /* (t1->flags & MPZN) != 0 */ pz = t1->mpg_i; - if (do_lint) { - if (mpz_sgn(pz) < 0) - lintwarn("%s", - mpg_fmt(_("%s: argument #%d negative value %Zd will give strange results"), + if (mpz_sgn(pz) < 0) + fatal("%s", + mpg_fmt(_("%s: argument #%d negative value %Zd is not allowed"), op, argnum, pz) ); - } + return pz; /* must not be freed */ } @@ -927,7 +919,7 @@ do_mpfr_lshift(int nargs) NODE *t1, *t2, *res; unsigned long shift; mpz_ptr pz1, pz2; - + t2 = POP_SCALAR(); t1 = POP_SCALAR(); @@ -959,7 +951,7 @@ do_mpfr_rshift(int nargs) NODE *t1, *t2, *res; unsigned long shift; mpz_ptr pz1, pz2; - + t2 = POP_SCALAR(); t1 = POP_SCALAR(); @@ -969,7 +961,7 @@ do_mpfr_rshift(int nargs) /* N.B: See do_mpfp_lshift. */ shift = mpz_get_ui(pz2); /* GMP integer => unsigned long conversion */ res = mpg_integer(); - mpz_fdiv_q_2exp(res->mpg_i, pz1, shift); /* res = pz1 / 2^shift, round towards −inf */ + mpz_fdiv_q_2exp(res->mpg_i, pz1, shift); /* res = pz1 / 2^shift, round towards -inf */ free_intval(t1, pz1); free_intval(t2, pz2); @@ -1080,25 +1072,22 @@ do_mpfr_strtonum(int nargs) { NODE *tmp, *r; - tmp = POP_SCALAR(); - if ((tmp->flags & (NUMBER|NUMCUR)) == 0) { + tmp = fixtype(POP_SCALAR()); + if ((tmp->flags & NUMBER) == 0) { r = mpg_integer(); /* will be changed to MPFR float if necessary in force_mpnum() */ r->stptr = tmp->stptr; r->stlen = tmp->stlen; force_mpnum(r, true, use_lc_numeric); r->stptr = NULL; r->stlen = 0; + } else if (is_mpg_float(tmp)) { + int tval; + r = mpg_float(); + tval = mpfr_set(r->mpg_numbr, tmp->mpg_numbr, ROUND_MODE); + IEEE_FMT(r->mpg_numbr, tval); } else { - (void) force_number(tmp); - if (is_mpg_float(tmp)) { - int tval; - r = mpg_float(); - tval = mpfr_set(r->mpg_numbr, tmp->mpg_numbr, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); - } else { - r = mpg_integer(); - mpz_set(r->mpg_i, tmp->mpg_i); - } + r = mpg_integer(); + mpz_set(r->mpg_i, tmp->mpg_i); } DEREF(tmp); @@ -1176,7 +1165,7 @@ do_mpfr_srand(int nargs) else { NODE *tmp; tmp = POP_SCALAR(); - if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) + if (do_lint && (fixtype(tmp)->flags & NUMBER) == 0) lintwarn(_("srand: received non-numeric argument")); force_number(tmp); if (is_mpg_float(tmp)) @@ -1190,6 +1179,95 @@ do_mpfr_srand(int nargs) return res; } +/* do_mpfr_intdiv --- do integer division, return quotient and remainder in dest array */ + +/* + * We define the semantics as: + * numerator = int(numerator) + * denominator = int(denonmator) + * quotient = int(numerator / denomator) + * remainder = int(numerator % denomator) + */ + +NODE * +do_mpfr_intdiv(int nargs) +{ + NODE *numerator, *denominator, *result; + NODE *num, *denom; + NODE *quotient, *remainder; + NODE *sub, **lhs; + + result = POP_PARAM(); + if (result->type != Node_var_array) + fatal(_("intdiv: third argument is not an array")); + assoc_clear(result); + + denominator = POP_SCALAR(); + numerator = POP_SCALAR(); + + if (do_lint) { + if ((fixtype(numerator)->flags & NUMBER) == 0) + lintwarn(_("intdiv: received non-numeric first argument")); + if ((fixtype(denominator)->flags & NUMBER) == 0) + lintwarn(_("intdiv: received non-numeric second argument")); + } + + (void) force_number(numerator); + (void) force_number(denominator); + + /* convert numerator and denominator to integer */ + if (is_mpg_integer(numerator)) { + num = mpg_integer(); + mpz_set(num->mpg_i, numerator->mpg_i); + } else { + if (! mpfr_number_p(numerator->mpg_numbr)) { + /* [+-]inf or NaN */ + return numerator; + } + + num = mpg_integer(); + mpfr_get_z(num->mpg_i, numerator->mpg_numbr, MPFR_RNDZ); + } + + if (is_mpg_integer(denominator)) { + denom = mpg_integer(); + mpz_set(denom->mpg_i, denominator->mpg_i); + } else { + if (! mpfr_number_p(denominator->mpg_numbr)) { + /* [+-]inf or NaN */ + return denominator; + } + + denom = mpg_integer(); + mpfr_get_z(denom->mpg_i, denominator->mpg_numbr, MPFR_RNDZ); + } + + if (mpz_sgn(denom->mpg_i) == 0) + fatal(_("intdiv: division by zero attempted")); + + quotient = mpg_integer(); + remainder = mpg_integer(); + + /* do the division */ + mpz_tdiv_qr(quotient->mpg_i, remainder->mpg_i, num->mpg_i, denom->mpg_i); + unref(num); + unref(denom); + unref(numerator); + unref(denominator); + + sub = make_string("quotient", 8); + lhs = assoc_lookup(result, sub); + unref(*lhs); + *lhs = quotient; + + sub = make_string("remainder", 9); + lhs = assoc_lookup(result, sub); + unref(*lhs); + *lhs = remainder; + + return make_number((AWKNUM) 0.0); +} + /* * mpg_tofloat --- convert an arbitrary-precision integer operand to * a float without loss of precision. It is assumed that the @@ -1204,13 +1282,13 @@ mpg_tofloat(mpfr_ptr mf, mpz_ptr mz) /* * When implicitely converting a GMP integer operand to a MPFR float, use * a precision sufficiently large to hold the converted value exactly. - * + * * $ ./gawk -M 'BEGIN { print 13 % 2 }' * 1 * If the user-specified precision is used to convert the integer 13 to a * float, one will get: * $ ./gawk -M 'BEGIN { PREC=2; print 13 % 2.0 }' - * 0 + * 0 */ prec = mpz_sizeinbase(mz, 2); /* most significant 1 bit position starting at 1 */ @@ -1224,7 +1302,7 @@ mpg_tofloat(mpfr_ptr mf, mpz_ptr mz) else prec = PRECISION_MIN; /* - * Always set the precision to avoid hysteresis, since do_mpfr_func + * Always set the precision to avoid hysteresis, since do_mpfr_func * may copy our precision. */ if (prec != mpfr_get_prec(mf)) @@ -1235,7 +1313,7 @@ mpg_tofloat(mpfr_ptr mf, mpz_ptr mz) } -/* mpg_add --- add arbitrary-precision numbers */ +/* mpg_add --- add arbitrary-precision numbers */ static NODE * mpg_add(NODE *t1, NODE *t2) @@ -1318,7 +1396,7 @@ mpg_mul(NODE *t1, NODE *t2) } -/* mpg_pow --- exponentiation involving arbitrary-precision numbers */ +/* mpg_pow --- exponentiation involving arbitrary-precision numbers */ static NODE * mpg_pow(NODE *t1, NODE *t2) @@ -1417,11 +1495,11 @@ mpg_mod(NODE *t1, NODE *t2) } return r; } - + /* * mpg_interpret --- pre-exec hook in the interpreter. Handles * arithmetic operations with MPFR/GMP numbers. - */ + */ static int mpg_interpret(INSTRUCTION **cp) @@ -1502,7 +1580,7 @@ quotient: if (op == Op_quotient) DEREF(t2); REPLACE(r); - break; + break; case Op_mod_i: t2 = force_number(pc->memory); |