aboutsummaryrefslogtreecommitdiffstats
path: root/mpfr.c
diff options
context:
space:
mode:
Diffstat (limited to 'mpfr.c')
-rw-r--r--mpfr.c1246
1 files changed, 875 insertions, 371 deletions
diff --git a/mpfr.c b/mpfr.c
index f1bf02c2..aaf65c2b 100644
--- a/mpfr.c
+++ b/mpfr.c
@@ -1,5 +1,5 @@
/*
- * mpfr.c - routines for MPFR number support in gawk.
+ * mpfr.c - routines for arbitrary-precision number support in gawk.
*/
/*
@@ -27,30 +27,53 @@
#ifdef HAVE_MPFR
-#if !defined(__GNU_MP_VERSION) || __GNU_MP_VERSION < 5
-typedef unsigned long int mp_bitcnt_t;
-#endif
-
#if !defined(MPFR_VERSION_MAJOR) || MPFR_VERSION_MAJOR < 3
typedef mp_exp_t mpfr_exp_t;
#endif
extern NODE **fmt_list; /* declared in eval.c */
-mpz_t mpzval; /* GMP integer type; used as temporary in many places */
-mpfr_t MNR;
-mpfr_t MFNR;
+mpz_t mpzval; /* GMP integer type, used as temporary in few places */
+mpz_t MNR;
+mpz_t MFNR;
int do_ieee_fmt; /* IEEE-754 floating-point emulation */
static mpfr_rnd_t get_rnd_mode(const char rmode);
-static NODE *get_bit_ops(NODE **p1, NODE **p2, const char *op);
static NODE *mpg_force_number(NODE *n);
static NODE *mpg_make_number(double);
static NODE *mpg_format_val(const char *format, int index, NODE *s);
static int mpg_interpret(INSTRUCTION **cp);
+
static mpfr_exp_t min_exp = MPFR_EMIN_DEFAULT;
static mpfr_exp_t max_exp = MPFR_EMAX_DEFAULT;
+/* temporaries used in bit ops */
+static NODE *_tz1;
+static NODE *_tz2;
+static mpz_t _mpz1;
+static mpz_t _mpz2;
+static mpz_ptr mpz1;
+static mpz_ptr mpz2;
+
+static NODE *get_bit_ops(const char *op);
+#define free_bit_ops() (DEREF(_tz1), DEREF(_tz2))
+
+/* temporary MPFR floats used to hold converted GMP integer operands */
+static mpfr_t _mpf_t1;
+static mpfr_t _mpf_t2;
+
+/*
+ * PRECISION_MIN is the precision used to initialize _mpf_t1 and _mpf_t2.
+ * 64 bits should be enough for exact conversion of most integers to floats.
+ */
+
+#define PRECISION_MIN 64
+
+/* mf = { _mpf_t1, _mpf_t2 } */
+static inline mpfr_ptr mpg_tofloat(mpfr_ptr mf, mpz_ptr mz);
+/* T = {t1, t2} */
+#define MP_FLOAT(T) is_mpg_integer(T) ? mpg_tofloat(_mpf_##T, (T)->mpg_i) : (T)->mpg_numbr
+
/* init_mpfr --- set up MPFR related variables */
@@ -63,29 +86,42 @@ init_mpfr(const char *rmode)
make_number = mpg_make_number;
str2number = mpg_force_number;
format_val = mpg_format_val;
- mpz_init(mpzval);
- mpfr_init(MNR);
- mpfr_set_d(MNR, 0.0, RND_MODE);
- mpfr_init(MFNR);
- mpfr_set_d(MFNR, 0.0, RND_MODE);
+ cmp_numbers = mpg_cmp;
+
+ mpz_init(MNR);
+ mpz_init(MFNR);
do_ieee_fmt = FALSE;
+
+ mpz_init(_mpz1);
+ mpz_init(_mpz2);
+ mpfr_init2(_mpf_t1, PRECISION_MIN);
+ mpfr_init2(_mpf_t2, PRECISION_MIN);
+ mpz_init(mpzval);
+
register_exec_hook(mpg_interpret, 0);
}
-/* mpg_node --- allocate a node to store a MPFR number */
+/* mpg_node --- allocate a node to store MPFR float or GMP integer */
NODE *
-mpg_node()
+mpg_node(unsigned int tp)
{
NODE *r;
getnode(r);
r->type = Node_val;
- /* Initialize, set precision to the default precision, and value to NaN */
- mpfr_init(r->mpg_numbr);
-
+ if (tp == MPFN) {
+ /* Initialize, set precision to the default precision, and value to NaN */
+ mpfr_init(r->mpg_numbr);
+ r->flags = MPFN;
+ } else {
+ /* Initialize and set value to 0 */
+ mpz_init(r->mpg_i);
+ r->flags = MPZN;
+ }
+
r->valref = 1;
- r->flags = MALLOC|MPFN|NUMBER|NUMCUR;
+ r->flags |= MALLOC|NUMBER|NUMCUR;
r->stptr = NULL;
r->stlen = 0;
#if MBS_SUPPORT
@@ -95,78 +131,238 @@ mpg_node()
return r;
}
-/* mpg_make_number --- make a MPFR number node and initialize with a double */
+/*
+ * mpg_make_number --- make a arbitrary-precision number node
+ * and initialize with a C double
+ */
static NODE *
mpg_make_number(double x)
{
NODE *r;
- int tval;
+ double ival;
- r = mpg_node();
- tval = mpfr_set_d(r->mpg_numbr, x, RND_MODE);
- IEEE_FMT(r->mpg_numbr, tval);
+ if ((ival = double_to_int(x)) != x) {
+ int tval;
+ r = mpg_float();
+ tval = mpfr_set_d(r->mpg_numbr, x, RND_MODE);
+ IEEE_FMT(r->mpg_numbr, tval);
+ } else {
+ r = mpg_integer();
+ mpz_set_d(r->mpg_i, ival);
+ }
return r;
}
-/* mpg_force_number --- force a value to be a MPFR number */
+/* mpg_strtoui --- assign arbitrary-precision integral value from a string */
-static NODE *
-mpg_force_number(NODE *n)
+int
+mpg_strtoui(mpz_ptr zi, char *str, size_t len, char **end, int base)
{
- char *cp, *cpend, *ptr;
- char save;
- int base = 10;
- unsigned int newflags = 0;
- int tval;
+ char *s = str;
+ char *start;
+ int ret = -1;
- if ((n->flags & (MPFN|NUMCUR)) == (MPFN|NUMCUR))
- return n;
+ /*
+ * mpz_set_str does not like leading 0x or 0X for hex (or 0 for octal)
+ * with a non-zero base argument.
+ */
+ if (base == 16 && len >= 2 && *s == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ s += 2; len -= 2;
+ } else if (base == 8 && len >= 1 && *s == '0') {
+ s++; len--;
+ }
+ start = s;
+
+ while (len > 0) {
+ switch (*s) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ break;
+ case '8':
+ case '9':
+ if (base == 8)
+ goto done;
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ if (base == 16)
+ break;
+ default:
+ goto done;
+ }
+ s++; len--;
+ }
+done:
+ if (s > start) {
+ char save = *s;
+ *s = '\0';
+ ret = mpz_set_str(zi, start, base);
+ *s = save;
+ }
+ if (end != NULL)
+ *end = s;
+ return ret;
+}
- if (n->flags & MAYBE_NUM) {
- n->flags &= ~MAYBE_NUM;
- newflags = NUMBER;
+
+/* mpg_maybe_float --- test if a string may contain arbitrary-precision float */
+
+static int
+mpg_maybe_float(const char *str, int use_locale)
+{
+ int dec_point = '.';
+ const char *s = str;
+
+#if defined(HAVE_LOCALE_H)
+ /*
+ * loc.decimal_point may not have been initialized yet,
+ * so double check it before using it.
+ */
+ if (use_locale && loc.decimal_point != NULL && loc.decimal_point[0] != '\0')
+ dec_point = loc.decimal_point[0]; /* XXX --- assumes one char */
+#endif
+
+ if (strlen(s) >= 3
+ && ( ( (s[0] == 'i' || s[0] == 'I')
+ && (s[1] == 'n' || s[1] == 'N')
+ && (s[2] == 'f' || s[2] == 'F'))
+ || ( (s[0] == 'n' || s[0] == 'N')
+ && (s[1] == 'a' || s[1] == 'A')
+ && (s[2] == 'n' || s[2] == 'N'))))
+ return TRUE;
+
+ for (; *s != '\0'; s++) {
+ if (*s == dec_point || *s == 'e' || *s == 'E')
+ return TRUE;
}
- if ((n->flags & MPFN) == 0) {
- n->flags |= MPFN;
- mpfr_init(n->mpg_numbr);
+ return FALSE;
+}
+
+
+/* mpg_zero --- initialize with arbitrary-precision integer(GMP) and set value to zero */
+
+static inline void
+mpg_zero(NODE *n)
+{
+ if (is_mpg_float(n)) {
+ mpfr_clear(n->mpg_numbr);
+ n->flags &= ~MPFN;
}
- mpfr_set_d(n->mpg_numbr, 0.0, RND_MODE);
+ if (! is_mpg_integer(n)) {
+ mpz_init(n->mpg_i); /* this also sets its value to 0 */
+ n->flags |= MPZN;
+ } else
+ mpz_set_si(n->mpg_i, 0);
+}
- if (n->stlen == 0)
- return n;
+
+/* force_mpnum --- force a value to be a GMP integer or MPFR float */
+
+static int
+force_mpnum(NODE *n, int do_nondec, int use_locale)
+{
+ char *cp, *cpend, *ptr, *cp1;
+ char save;
+ int tval, base = 10;
+
+ if (n->stlen == 0) {
+ mpg_zero(n);
+ return FALSE;
+ }
cp = n->stptr;
cpend = n->stptr + n->stlen;
while (cp < cpend && isspace((unsigned char) *cp))
cp++;
- if (cp == cpend) /* only spaces */
- return n;
-
+ if (cp == cpend) { /* only spaces */
+ mpg_zero(n);
+ return FALSE;
+ }
+
save = *cpend;
*cpend = '\0';
- if (do_non_decimal_data && ! do_traditional)
- base = get_numbase(cp, TRUE);
+ if (*cp == '+' || *cp == '-')
+ cp1 = cp + 1;
+ else
+ cp1 = cp;
+
+ if (do_nondec)
+ base = get_numbase(cp1, use_locale);
+
+ if (! mpg_maybe_float(cp1, use_locale)) {
+ mpg_zero(n);
+ errno = 0;
+ mpg_strtoui(n->mpg_i, cp1, cpend - cp1, & ptr, base);
+ if (*cp == '-')
+ mpz_neg(n->mpg_i, n->mpg_i);
+ goto done;
+ }
+
+ if (is_mpg_integer(n)) {
+ mpz_clear(n->mpg_i);
+ n->flags &= ~MPZN;
+ }
+
+ if (! is_mpg_float(n)) {
+ mpfr_init(n->mpg_numbr);
+ n->flags |= MPFN;
+ }
errno = 0;
tval = mpfr_strtofr(n->mpg_numbr, cp, & ptr, base, RND_MODE);
IEEE_FMT(n->mpg_numbr, tval);
-
+done:
/* trailing space is OK for NUMBER */
while (isspace((unsigned char) *ptr))
ptr++;
*cpend = save;
- if (errno == 0 && ptr == cpend) {
+ if (errno == 0 && ptr == cpend)
+ return TRUE;
+ errno = 0;
+ return FALSE;
+}
+
+/* mpg_force_number --- force a value to be a multiple-precision number */
+
+static NODE *
+mpg_force_number(NODE *n)
+{
+ unsigned int newflags = 0;
+
+ if (is_mpg_number(n) && (n->flags & NUMCUR))
+ return n;
+
+ if (n->flags & MAYBE_NUM) {
+ n->flags &= ~MAYBE_NUM;
+ newflags = NUMBER;
+ }
+
+ if (force_mpnum(n, (do_non_decimal_data && ! do_traditional), TRUE)) {
n->flags |= newflags;
n->flags |= NUMCUR;
}
- errno = 0;
return n;
}
-
/* mpg_format_val --- format a numeric value based on format */
static NODE *
@@ -179,7 +375,7 @@ mpg_format_val(const char *format, int index, NODE *s)
dummy[1] = s;
oflags = s->flags;
- if (mpfr_integer_p(s->mpg_numbr)) {
+ 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;
@@ -200,80 +396,107 @@ mpg_format_val(const char *format, int index, NODE *s)
return s;
}
+/* mpg_cmp --- compare two numbers */
+
+int
+mpg_cmp(const NODE *t1, const NODE *t2)
+{
+ /*
+ * For the purposes of sorting, NaN is considered greater than
+ * any other value, and all NaN values are considered equivalent and equal.
+ */
+
+ if (is_mpg_float(t1)) {
+ if (is_mpg_float(t2)) {
+ if (mpfr_nan_p(t1->mpg_numbr))
+ return ! mpfr_nan_p(t2->mpg_numbr);
+ if (mpfr_nan_p(t2->mpg_numbr))
+ return -1;
+ return mpfr_cmp(t1->mpg_numbr, t2->mpg_numbr);
+ }
+ if (mpfr_nan_p(t1->mpg_numbr))
+ return 1;
+ return mpfr_cmp_z(t1->mpg_numbr, t2->mpg_i);
+ } else if (is_mpg_float(t2)) {
+ int ret;
+ if (mpfr_nan_p(t2->mpg_numbr))
+ return -1;
+ ret = mpfr_cmp_z(t2->mpg_numbr, t1->mpg_i);
+ return ret > 0 ? -1 : (ret < 0);
+ } else if (is_mpg_integer(t1)) {
+ return mpz_cmp(t1->mpg_i, t2->mpg_i);
+ }
+
+ /* t1 and t2 are AWKNUMs */
+ return cmp_awknums(t1, t2);
+}
+
/*
* mpg_update_var --- update NR or FNR.
- * NR_node->var_value(mpfr_t) = MNR(mpfr_t) * LONG_MAX + NR(long)
+ * NR_node->var_value(mpz_t) = MNR(mpz_t) * LONG_MAX + NR(long)
*/
-void
+NODE *
mpg_update_var(NODE *n)
{
NODE *val = n->var_value;
- long nl;
- mpfr_ptr nm;
+ long nr;
+ mpz_ptr nq;
if (n == NR_node) {
- nl = NR;
- nm = MNR;
+ nr = NR;
+ nq = MNR;
} else if (n == FNR_node) {
- nl = FNR;
- nm = MFNR;
+ nr = FNR;
+ nq = MFNR;
} else
cant_happen();
- if (mpfr_zero_p(nm)) {
- double d;
-
- /* Efficiency hack for NR < LONG_MAX */
- d = mpfr_get_d(val->mpg_numbr, RND_MODE);
- if (d != nl) {
+ if (mpz_sgn(nq) == 0) {
+ /* Efficiency hack similar to that for AWKNUM */
+ if (is_mpg_float(val) || mpz_get_si(val->mpg_i) != nr) {
unref(n->var_value);
- n->var_value = make_number(nl);
+ val = n->var_value = mpg_integer();
+ mpz_set_si(val->mpg_i, nr);
}
} else {
unref(n->var_value);
- val = n->var_value = mpg_node();
- mpfr_mul_si(val->mpg_numbr, nm, LONG_MAX, RND_MODE);
- mpfr_add_si(val->mpg_numbr, val->mpg_numbr, nl, RND_MODE);
+ val = n->var_value = mpg_integer();
+ mpz_set_si(val->mpg_i, nr);
+ mpz_addmul_ui(val->mpg_i, nq, LONG_MAX); /* val->mpg_i += nq * LONG_MAX */
}
+ return val;
}
-
/* mpg_set_var --- set NR or FNR */
long
mpg_set_var(NODE *n)
{
- long l;
- mpfr_ptr nm;
- mpfr_ptr p = n->var_value->mpg_numbr;
+ long nr;
+ mpz_ptr nq, r;
+ NODE *val = n->var_value;
int neg = FALSE;
if (n == NR_node)
- nm = MNR;
+ nq = MNR;
else if (n == FNR_node)
- nm = MFNR;
+ nq = MFNR;
else
cant_happen();
- mpfr_get_z(mpzval, p, MPFR_RNDZ);
- if (mpfr_signbit(p)) {
- /* It is a negative number ! */
- neg = TRUE;
- mpz_neg(mpzval, mpzval);
- }
- l = mpz_fdiv_q_ui(mpzval, mpzval, LONG_MAX);
- if (neg) {
- mpz_neg(mpzval, mpzval);
- l = -l;
+ if (is_mpg_integer(val))
+ r = val->mpg_i;
+ else {
+ /* convert float to integer */
+ mpfr_get_z(mpzval, val->mpg_numbr, MPFR_RNDZ);
+ r = mpzval;
}
-
- mpfr_set_z(nm, mpzval, RND_MODE); /* quotient (MNR) */
- return l; /* remainder (NR) */
+ nr = mpz_fdiv_q_ui(nq, r, LONG_MAX); /* nq (MNR or MFNR) is quotient */
+ return nr; /* remainder (NR or FNR) */
}
-
/* set_PREC --- update MPFR PRECISION related variables when PREC assigned to */
void
@@ -358,20 +581,20 @@ get_rnd_mode(const char rmode)
switch (rmode) {
case 'N':
case 'n':
- return MPFR_RNDN; /* round to nearest */
+ return MPFR_RNDN; /* round to nearest (IEEE-754 roundTiesToEven) */
case 'Z':
case 'z':
- return MPFR_RNDZ; /* round toward zero */
+ return MPFR_RNDZ; /* round toward zero (IEEE-754 roundTowardZero) */
case 'U':
case 'u':
- return MPFR_RNDU; /* round toward plus infinity */
+ return MPFR_RNDU; /* round toward plus infinity (IEEE-754 roundTowardPositive) */
case 'D':
case 'd':
- return MPFR_RNDD; /* round toward minus infinity */
+ return MPFR_RNDD; /* round toward minus infinity (IEEE-754 roundTowardNegative) */
#if defined(MPFR_VERSION_MAJOR) && MPFR_VERSION_MAJOR > 2
case 'A':
case 'a':
- return MPFR_RNDA; /* round away from zero */
+ return MPFR_RNDA; /* round away from zero (IEEE-754 roundTiesToAway) */
#endif
default:
break;
@@ -407,12 +630,12 @@ format_ieee(mpfr_ptr x, int tval)
/*
* The MPFR doc says that it's our responsibility to make sure all numbers
* including those previously created are in range after we've changed the
- * exponent range. Most MPFR operations and functions requires
+ * exponent range. Most MPFR operations and functions require
* the input arguments to have exponents within the current exponent range.
* Any argument outside the range results in a MPFR assertion failure
* like this:
*
- * $] gawk -M 'BEGIN { x=1.0e-10000; print x+0; PREC="double"; print x+0}'
+ * $ gawk -M 'BEGIN { x=1.0e-10000; print x+0; PREC="double"; print x+0}'
* 1e-10000
* init2.c:52: MPFR assertion failed ....
*
@@ -423,7 +646,7 @@ format_ieee(mpfr_ptr x, int tval)
*
* When gawk starts, the exponent range is the MPFR default
* [MPFR_EMIN_DEFAULT, MPFR_EMAX_DEFAULT]. Any number that gawk
- * creates must have exponent in this range (excluding infinities, NANs and zeros).
+ * creates must have exponent in this range (excluding infinities, NaNs and zeros).
* Each MPFR operation or function is performed with this default exponent
* range.
*
@@ -444,86 +667,13 @@ format_ieee(mpfr_ptr x, int tval)
}
-/* get_bit_ops --- get the numeric operands of a binary function */
-
-static NODE *
-get_bit_ops(NODE **p1, NODE **p2, const char *op)
-{
- NODE *t1, *t2;
- mpfr_ptr left, right;
-
- *p2 = t2 = POP_SCALAR();
- *p1 = t1 = POP_SCALAR();
-
- if (do_lint) {
- if ((t1->flags & (NUMCUR|NUMBER)) == 0)
- lintwarn(_("%s: received non-numeric first argument"), op);
- if ((t2->flags & (NUMCUR|NUMBER)) == 0)
- lintwarn(_("%s: received non-numeric second argument"), op);
- }
-
- left = force_number(t1)->mpg_numbr;
- right = force_number(t2)->mpg_numbr;
-
- if (! mpfr_number_p(left)) {
- /* [+-]inf or NaN */
- DEREF(t2);
- return t1;
- }
-
- if (! mpfr_number_p(right)) {
- /* [+-]inf or NaN */
- DEREF(t1);
- return t2;
- }
-
- if (do_lint) {
- if (mpfr_signbit(left) || mpfr_signbit(right))
- lintwarn("%s",
- mpg_fmt(_("%s(%Rg, %Rg): negative values will give strange results"),
- op, left, right)
- );
- if (! mpfr_integer_p(left) || ! mpfr_integer_p(right))
- lintwarn("%s",
- mpg_fmt(_("%s(%Rg, %Rg): fractional values will be truncated"),
- op, left, right)
- );
- }
- return NULL;
-}
-
-
-/* do_mpfr_and --- perform an & operation */
-
-NODE *
-do_mpfr_and(int nargs)
-{
- NODE *t1, *t2, *res;
- mpz_t z;
-
- if ((res = get_bit_ops(& t1, & t2, "and")) != NULL)
- return res;
-
- mpz_init(z);
- mpfr_get_z(mpzval, t1->mpg_numbr, MPFR_RNDZ); /* float to integer conversion */
- mpfr_get_z(z, t2->mpg_numbr, MPFR_RNDZ); /* Same */
- mpz_and(z, mpzval, z);
-
- res = mpg_node();
- mpfr_set_z(res->mpg_numbr, z, RND_MODE); /* integer to float conversion */
- mpz_clear(z);
-
- DEREF(t1);
- DEREF(t2);
- return res;
-}
-
/* do_mpfr_atan2 --- do the atan2 function */
NODE *
do_mpfr_atan2(int nargs)
{
NODE *t1, *t2, *res;
+ mpfr_ptr p1, p2;
int tval;
t2 = POP_SCALAR();
@@ -538,9 +688,11 @@ do_mpfr_atan2(int nargs)
force_number(t1);
force_number(t2);
- res = mpg_node();
+ p1 = MP_FLOAT(t1);
+ p2 = MP_FLOAT(t2);
+ res = mpg_float();
/* See MPFR documentation for handling of special values like +inf as an argument */
- tval = mpfr_atan2(res->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, RND_MODE);
+ tval = mpfr_atan2(res->mpg_numbr, p1, p2, RND_MODE);
IEEE_FMT(res->mpg_numbr, tval);
DEREF(t1);
@@ -549,53 +701,19 @@ do_mpfr_atan2(int nargs)
}
-/* do_mpfr_compl --- perform a ~ operation */
-
-NODE *
-do_mpfr_compl(int nargs)
-{
- NODE *tmp, *r;
- mpfr_ptr p;
-
- tmp = POP_SCALAR();
- if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
- lintwarn(_("compl: received non-numeric argument"));
-
- p = force_number(tmp)->mpg_numbr;
- if (! mpfr_number_p(p)) {
- /* [+-]inf or NaN */
- return tmp;
- }
-
- if (do_lint) {
- if (mpfr_signbit(p))
- lintwarn("%s",
- mpg_fmt(_("compl(%Rg): negative value will give strange results"), p)
- );
- if (! mpfr_integer_p(p))
- lintwarn("%s",
- mpg_fmt(_("comp(%Rg): fractional value will be truncated"), p)
- );
- }
- mpfr_get_z(mpzval, p, MPFR_RNDZ);
- mpz_com(mpzval, mpzval);
- r = mpg_node();
- mpfr_set_z(r->mpg_numbr, mpzval, RND_MODE);
- DEREF(tmp);
- return r;
-}
-
-#define SPEC_MATH(X) \
-NODE *tmp, *res; \
-int tval; \
-tmp = POP_SCALAR(); \
-if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) \
- lintwarn(_("%s: received non-numeric argument"), #X); \
-force_number(tmp); \
-res = mpg_node(); \
-tval = mpfr_##X(res->mpg_numbr, tmp->mpg_numbr, RND_MODE); \
-IEEE_FMT(res->mpg_numbr, tval); \
-DEREF(tmp); \
+#define SPEC_MATH(X) \
+NODE *t1, *res; \
+mpfr_ptr p1; \
+int tval; \
+t1 = POP_SCALAR(); \
+if (do_lint && (t1->flags & (NUMCUR|NUMBER)) == 0) \
+ lintwarn(_("%s: received non-numeric argument"), #X); \
+force_number(t1); \
+p1 = MP_FLOAT(t1); \
+res = mpg_float(); \
+tval = mpfr_##X(res->mpg_numbr, p1, RND_MODE); \
+IEEE_FMT(res->mpg_numbr, tval); \
+DEREF(t1); \
return res
@@ -650,89 +768,251 @@ do_mpfr_int(int nargs)
if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
lintwarn(_("int: received non-numeric argument"));
force_number(tmp);
- if (! mpfr_number_p(tmp->mpg_numbr)) {
- /* [+-]inf or NaN */
- return tmp;
+
+ if (is_mpg_integer(tmp)) {
+ r = mpg_integer();
+ mpz_set(r->mpg_i, tmp->mpg_i);
+ } else {
+ if (! mpfr_number_p(tmp->mpg_numbr)) {
+ /* [+-]inf or NaN */
+ return tmp;
+ }
+
+ r = mpg_integer();
+ mpfr_get_z(r->mpg_i, tmp->mpg_numbr, MPFR_RNDZ);
}
- mpfr_get_z(mpzval, tmp->mpg_numbr, MPFR_RNDZ);
- r = mpg_node();
- mpfr_set_z(r->mpg_numbr, mpzval, RND_MODE);
+
DEREF(tmp);
return r;
}
-
-/* do_mpfr_lshift --- perform a << operation */
+/* do_mpfr_compl --- perform a ~ operation */
NODE *
-do_mpfr_lshift(int nargs)
+do_mpfr_compl(int nargs)
{
- NODE *t1, *t2, *res;
- mp_bitcnt_t shift;
+ NODE *tmp, *r;
+ mpz_ptr zptr;
- if ((res = get_bit_ops(& t1, & t2, "lshift")) != NULL)
- return res;
+ tmp = POP_SCALAR();
+ if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
+ lintwarn(_("compl: received non-numeric argument"));
- mpfr_get_z(mpzval, t1->mpg_numbr, MPFR_RNDZ); /* mpfr_t (float) => mpz_t (integer) conversion */
- shift = mpfr_get_ui(t2->mpg_numbr, MPFR_RNDZ); /* mpfr_t (float) => unsigned long conversion */
- mpz_mul_2exp(mpzval, mpzval, shift); /* mpzval = mpzval * 2^shift */
+ force_number(tmp);
+ if (is_mpg_float(tmp)) {
+ mpfr_ptr p = tmp->mpg_numbr;
- res = mpg_node();
- mpfr_set_z(res->mpg_numbr, mpzval, RND_MODE); /* integer to float conversion */
- DEREF(t1);
- DEREF(t2);
- return res;
+ if (! mpfr_number_p(p)) {
+ /* [+-]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_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 */
+ 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)
+ );
+ }
+ }
+
+ r = mpg_integer();
+ mpz_com(r->mpg_i, zptr);
+ DEREF(tmp);
+ return r;
}
-/* do_mpfr_or --- perform an | operation */
+/*
+ * get_bit_ops --- get the numeric operands of a binary function.
+ * Returns a copy of the operand if either is inf or nan. Otherwise
+ * each operand is converted to an integer if necessary, and
+ * the results are placed in the variables mpz1 and mpz2.
+ */
-NODE *
-do_mpfr_or(int nargs)
+static NODE *
+get_bit_ops(const char *op)
{
- NODE *t1, *t2, *res;
- mpz_t z;
+ _tz2 = POP_SCALAR();
+ _tz1 = POP_SCALAR();
- if ((res = get_bit_ops(& t1, & t2, "or")) != NULL)
- return res;
+ if (do_lint) {
+ if ((_tz1->flags & (NUMCUR|NUMBER)) == 0)
+ lintwarn(_("%s: received non-numeric first argument"), op);
+ if ((_tz2->flags & (NUMCUR|NUMBER)) == 0)
+ lintwarn(_("%s: received non-numeric second argument"), op);
+ }
- mpz_init(z);
- mpfr_get_z(mpzval, t1->mpg_numbr, MPFR_RNDZ);
- mpfr_get_z(z, t2->mpg_numbr, MPFR_RNDZ);
- mpz_ior(z, mpzval, z);
+ force_number(_tz1);
+ force_number(_tz2);
+
+ if (is_mpg_float(_tz1)) {
+ mpfr_ptr left = _tz1->mpg_numbr;
+ if (! mpfr_number_p(left)) {
+ /* inf or NaN */
+ NODE *res;
+ res = mpg_float();
+ mpfr_set(res->mpg_numbr, _tz1->mpg_numbr, RND_MODE);
+ return res;
+ }
- res = mpg_node();
- mpfr_set_z(res->mpg_numbr, z, RND_MODE);
- mpz_clear(z);
+ if (do_lint) {
+ if (mpfr_sgn(left) < 0)
+ lintwarn("%s",
+ mpg_fmt(_("%s(%Rg, ..): negative values will give strange results"),
+ op, left)
+ );
+ if (! mpfr_integer_p(left))
+ lintwarn("%s",
+ mpg_fmt(_("%s(%Rg, ..): fractional values will be truncated"),
+ op, left)
+ );
+ }
+
+ mpfr_get_z(_mpz1, left, MPFR_RNDZ); /* float to integer conversion */
+ mpz1 = _mpz1;
+ } else {
+ /* (_tz1->flags & MPZN) != 0 */
+ mpz1 = _tz1->mpg_i;
+ if (do_lint) {
+ if (mpz_sgn(mpz1) < 0)
+ lintwarn("%s",
+ mpg_fmt(_("%s(%Zd, ..): negative values will give strange results"),
+ op, mpz1)
+ );
+ }
+ }
- DEREF(t1);
- DEREF(t2);
- return res;
+ if (is_mpg_float(_tz2)) {
+ mpfr_ptr right = _tz2->mpg_numbr;
+ if (! mpfr_number_p(right)) {
+ /* inf or NaN */
+ NODE *res;
+ res = mpg_float();
+ mpfr_set(res->mpg_numbr, _tz2->mpg_numbr, RND_MODE);
+ return res;
+ }
+
+ if (do_lint) {
+ if (mpfr_sgn(right) < 0)
+ lintwarn("%s",
+ mpg_fmt(_("%s(.., %Rg): negative values will give strange results"),
+ op, right)
+ );
+ if (! mpfr_integer_p(right))
+ lintwarn("%s",
+ mpg_fmt(_("%s(.., %Rg): fractional values will be truncated"),
+ op, right)
+ );
+ }
+
+ mpfr_get_z(_mpz2, right, MPFR_RNDZ); /* float to integer conversion */
+ mpz2 = _mpz2;
+ } else {
+ /* (_tz2->flags & MPZN) != 0 */
+ mpz2 = _tz2->mpg_i;
+ if (do_lint) {
+ if (mpz_sgn(mpz2) < 0)
+ lintwarn("%s",
+ mpg_fmt(_("%s(.., %Zd): negative values will give strange results"),
+ op, mpz2)
+ );
+ }
+ }
+
+ return NULL;
}
+/* do_mpfr_lshift --- perform a << operation */
+
+NODE *
+do_mpfr_lshift(int nargs)
+{
+ NODE *res;
+ unsigned long shift;
+
+ if ((res = get_bit_ops("lshift")) == NULL) {
+
+ /*
+ * mpz_get_ui: If op is too big to fit an unsigned long then just
+ * the least significant bits that do fit are returned.
+ * The sign of op is ignored, only the absolute value is used.
+ */
+
+ shift = mpz_get_ui(mpz2); /* GMP integer => unsigned long conversion */
+ res = mpg_integer();
+ mpz_mul_2exp(res->mpg_i, mpz1, shift); /* res = mpz1 * 2^shift */
+ }
+ free_bit_ops();
+ return res;
+}
/* do_mpfr_rshift --- perform a >> operation */
NODE *
do_mpfr_rhift(int nargs)
{
- NODE *t1, *t2, *res;
- mp_bitcnt_t shift;
+ NODE *res;
+ unsigned long shift;
+
+ if ((res = get_bit_ops("rshift")) == NULL) {
+ /*
+ * mpz_get_ui: If op is too big to fit an unsigned long then just
+ * the least significant bits that do fit are returned.
+ * The sign of op is ignored, only the absolute value is used.
+ */
+
+ shift = mpz_get_ui(mpz2); /* GMP integer => unsigned long conversion */
+ res = mpg_integer();
+ mpz_fdiv_q_2exp(res->mpg_i, mpz1, shift); /* res = mpz1 / 2^shift, round towards −inf */
+ }
+ free_bit_ops();
+ return res;
+}
- if ((res = get_bit_ops(& t1, & t2, "rshift")) != NULL)
- return res;
+/* do_mpfr_and --- perform an & operation */
- mpfr_get_z(mpzval, t1->mpg_numbr, MPFR_RNDZ); /* mpfr_t (float) => mpz_t (integer) conversion */
- shift = mpfr_get_ui(t2->mpg_numbr, MPFR_RNDZ); /* mpfr_t (float) => unsigned long conversion */
- mpz_fdiv_q_2exp(mpzval, mpzval, shift); /* mpzval = mpzval / 2^shift, round towards −inf */
+NODE *
+do_mpfr_and(int nargs)
+{
+ NODE *res;
- res = mpg_node();
- mpfr_set_z(res->mpg_numbr, mpzval, RND_MODE); /* integer to float conversion */
- DEREF(t1);
- DEREF(t2);
+ if ((res = get_bit_ops("and")) == NULL) {
+ res = mpg_integer();
+ mpz_and(res->mpg_i, mpz1, mpz2);
+ }
+ free_bit_ops();
return res;
}
+/* do_mpfr_or --- perform an | operation */
+
+NODE *
+do_mpfr_or(int nargs)
+{
+ NODE *res;
+
+ if ((res = get_bit_ops("or")) == NULL) {
+ res = mpg_integer();
+ mpz_ior(res->mpg_i, mpz1, mpz2);
+ }
+ free_bit_ops();
+ return res;
+}
/* do_mpfr_strtonum --- the strtonum function */
@@ -740,48 +1020,44 @@ NODE *
do_mpfr_strtonum(int nargs)
{
NODE *tmp, *r;
- int base, tval;
tmp = POP_SCALAR();
- r = mpg_node();
- if ((tmp->flags & (NUMBER|NUMCUR)) != 0)
- tval = mpfr_set(r->mpg_numbr, tmp->mpg_numbr, RND_MODE);
- else if ((base = get_numbase(tmp->stptr, use_lc_numeric)) != 10) {
- tval = mpfr_strtofr(r->mpg_numbr, tmp->stptr, NULL, base, RND_MODE);
- errno = 0;
+ if ((tmp->flags & (NUMBER|NUMCUR)) == 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 {
(void) force_number(tmp);
- tval = mpfr_set(r->mpg_numbr, tmp->mpg_numbr, RND_MODE);
+ if (is_mpg_float(tmp)) {
+ int tval;
+ r = mpg_float();
+ tval = mpfr_set(r->mpg_numbr, tmp->mpg_numbr, RND_MODE);
+ IEEE_FMT(r->mpg_numbr, tval);
+ } else {
+ r = mpg_integer();
+ mpz_set(r->mpg_i, tmp->mpg_i);
+ }
}
- IEEE_FMT(r->mpg_numbr, tval);
DEREF(tmp);
return r;
}
-
/* do_mpfr_xor --- perform an ^ operation */
NODE *
do_mpfr_xor(int nargs)
{
- NODE *t1, *t2, *res;
- mpz_t z;
-
- if ((res = get_bit_ops(& t1, & t2, "xor")) != NULL)
- return res;
-
- mpz_init(z);
- mpfr_get_z(mpzval, t1->mpg_numbr, MPFR_RNDZ);
- mpfr_get_z(z, t2->mpg_numbr, MPFR_RNDZ);
- mpz_xor(z, mpzval, z);
-
- res = mpg_node();
- mpfr_set_z(res->mpg_numbr, z, RND_MODE);
- mpz_clear(z);
+ NODE *res;
- DEREF(t1);
- DEREF(t2);
+ if ((res = get_bit_ops("xor")) == NULL) {
+ res = mpg_integer();
+ mpz_xor(res->mpg_i, mpz1, mpz2);
+ }
+ free_bit_ops();
return res;
}
@@ -799,15 +1075,24 @@ do_mpfr_rand(int nargs ATTRIBUTE_UNUSED)
int tval;
if (firstrand) {
+#if 0
/* Choose the default algorithm */
gmp_randinit_default(state);
+#endif
+ /*
+ * Choose a specific (Mersenne Twister) algorithm in case the default
+ * changes in the future.
+ */
+
+ gmp_randinit_mt(state);
+
mpz_init(seed);
- mpz_set_ui(seed, 1L);
+ mpz_set_ui(seed, 1);
/* seed state */
gmp_randseed(state, seed);
firstrand = FALSE;
}
- res = mpg_node();
+ res = mpg_float();
tval = mpfr_urandomb(res->mpg_numbr, state);
IEEE_FMT(res->mpg_numbr, tval);
return res;
@@ -820,20 +1105,27 @@ NODE *
do_mpfr_srand(int nargs)
{
NODE *res;
- int tval;
if (firstrand) {
+#if 0
/* Choose the default algorithm */
gmp_randinit_default(state);
+#endif
+ /*
+ * Choose a specific algorithm (Mersenne Twister) in case default
+ * changes in the future.
+ */
+
+ gmp_randinit_mt(state);
+
mpz_init(seed);
- mpz_set_ui(seed, 1L);
+ mpz_set_ui(seed, 1);
/* No need to seed state, will change it below */
firstrand = FALSE;
}
- res = mpg_node();
- tval = mpfr_set_z(res->mpg_numbr, seed, RND_MODE); /* previous seed */
- IEEE_FMT(res->mpg_numbr, tval);
+ res = mpg_integer();
+ mpz_set(res->mpg_i, seed); /* previous seed */
if (nargs == 0)
mpz_set_ui(seed, (unsigned long) time((time_t *) 0));
@@ -843,7 +1135,10 @@ do_mpfr_srand(int nargs)
if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
lintwarn(_("srand: received non-numeric argument"));
force_number(tmp);
- mpfr_get_z(seed, tmp->mpg_numbr, MPFR_RNDZ);
+ if (is_mpg_float(tmp))
+ mpfr_get_z(seed, tmp->mpg_numbr, MPFR_RNDZ);
+ else /* MP integer */
+ mpz_set(seed, tmp->mpg_i);
DEREF(tmp);
}
@@ -852,8 +1147,209 @@ do_mpfr_srand(int nargs)
}
/*
+ * mpg_tofloat --- convert an arbitrary-precision integer operand to
+ * a float without loss of precision. It is assumed that the
+ * MPFR variable has already been initialized.
+ */
+
+static inline mpfr_ptr
+mpg_tofloat(mpfr_ptr mf, mpz_ptr mz)
+{
+ size_t prec;
+
+ /*
+ * 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
+ */
+
+ prec = mpz_sizeinbase(mz, 2); /* most significant 1 bit position starting at 1 */
+ if (prec > PRECISION_MIN) {
+ prec -= (size_t) mpz_scan1(mz, 0); /* least significant 1 bit index starting at 0 */
+ if (prec > MPFR_PREC_MAX)
+ prec = MPFR_PREC_MAX;
+ if (prec > PRECISION_MIN)
+ mpfr_set_prec(mf, prec);
+ }
+
+ mpfr_set_z(mf, mz, RND_MODE);
+ return mf;
+}
+
+
+/* mpg_add --- add arbitrary-precision numbers */
+
+static NODE *
+mpg_add(NODE *t1, NODE *t2)
+{
+ NODE *r;
+ int tval;
+
+ if (is_mpg_integer(t1) && is_mpg_integer(t2)) {
+ r = mpg_integer();
+ mpz_add(r->mpg_i, t1->mpg_i, t2->mpg_i);
+ } else {
+ r = mpg_float();
+ if (is_mpg_integer(t2))
+ tval = mpfr_add_z(r->mpg_numbr, t1->mpg_numbr, t2->mpg_i, RND_MODE);
+ else if (is_mpg_integer(t1))
+ tval = mpfr_add_z(r->mpg_numbr, t2->mpg_numbr, t1->mpg_i, RND_MODE);
+ else
+ tval = mpfr_add(r->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, RND_MODE);
+ IEEE_FMT(r->mpg_numbr, tval);
+ }
+ return r;
+}
+
+/* mpg_sub --- subtract arbitrary-precision numbers */
+
+static NODE *
+mpg_sub(NODE *t1, NODE *t2)
+{
+ NODE *r;
+ int tval;
+
+ if (is_mpg_integer(t1) && is_mpg_integer(t2)) {
+ r = mpg_integer();
+ mpz_sub(r->mpg_i, t1->mpg_i, t2->mpg_i);
+ } else {
+ r = mpg_float();
+ if (is_mpg_integer(t2))
+ tval = mpfr_sub_z(r->mpg_numbr, t1->mpg_numbr, t2->mpg_i, RND_MODE);
+ else if (is_mpg_integer(t1)) {
+#if (!defined(MPFR_VERSION) || (MPFR_VERSION < MPFR_VERSION_NUM(3,1,0)))
+ NODE *tmp = t1;
+ t1 = t2;
+ t2 = tmp;
+ tval = mpfr_sub_z(r->mpg_numbr, t1->mpg_numbr, t2->mpg_i, RND_MODE);
+ tval = mpfr_neg(r->mpg_numbr, r->mpg_numbr, RND_MODE);
+ t2 = t1;
+ t1 = tmp;
+#else
+ tval = mpfr_z_sub(r->mpg_numbr, t1->mpg_i, t2->mpg_numbr, RND_MODE);
+#endif
+ } else
+ tval = mpfr_sub(r->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, RND_MODE);
+ IEEE_FMT(r->mpg_numbr, tval);
+ }
+ return r;
+}
+
+/* mpg_mul --- multiply arbitrary-precision numbers */
+
+static NODE *
+mpg_mul(NODE *t1, NODE *t2)
+{
+ NODE *r;
+ int tval;
+
+ if (is_mpg_integer(t1) && is_mpg_integer(t2)) {
+ r = mpg_integer();
+ mpz_mul(r->mpg_i, t1->mpg_i, t2->mpg_i);
+ } else {
+ r = mpg_float();
+ if (is_mpg_integer(t2))
+ tval = mpfr_mul_z(r->mpg_numbr, t1->mpg_numbr, t2->mpg_i, RND_MODE);
+ else if (is_mpg_integer(t1))
+ tval = mpfr_mul_z(r->mpg_numbr, t2->mpg_numbr, t1->mpg_i, RND_MODE);
+ else
+ tval = mpfr_mul(r->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, RND_MODE);
+ IEEE_FMT(r->mpg_numbr, tval);
+ }
+ return r;
+}
+
+
+/* mpg_pow --- exponentiation involving arbitrary-precision numbers */
+
+static NODE *
+mpg_pow(NODE *t1, NODE *t2)
+{
+ NODE *r;
+ int tval;
+
+ if (is_mpg_integer(t1) && is_mpg_integer(t2)) {
+ if (mpz_sgn(t2->mpg_i) >= 0 && mpz_fits_ulong_p(t2->mpg_i)) {
+ r = mpg_integer();
+ mpz_pow_ui(r->mpg_i, t1->mpg_i, mpz_get_ui(t2->mpg_i));
+ } else {
+ mpfr_ptr p1, p2;
+ p1 = MP_FLOAT(t1);
+ p2 = MP_FLOAT(t2);
+ r = mpg_float();
+ tval = mpfr_pow(r->mpg_numbr, p1, p2, RND_MODE);
+ IEEE_FMT(r->mpg_numbr, tval);
+ }
+ } else {
+ r = mpg_float();
+ if (is_mpg_integer(t2))
+ tval = mpfr_pow_z(r->mpg_numbr, t1->mpg_numbr, t2->mpg_i, RND_MODE);
+ else {
+ mpfr_ptr p1;
+ p1 = MP_FLOAT(t1);
+ tval = mpfr_pow(r->mpg_numbr, p1, t2->mpg_numbr, RND_MODE);
+ }
+ IEEE_FMT(r->mpg_numbr, tval);
+ }
+ return r;
+}
+
+/* mpg_div --- arbitrary-precision division */
+
+static NODE *
+mpg_div(NODE *t1, NODE *t2)
+{
+ NODE *r;
+ int tval;
+
+ if (is_mpg_integer(t1) && is_mpg_integer(t2)
+ && (mpz_sgn(t2->mpg_i) != 0) /* not dividing by 0 */
+ && mpz_divisible_p(t1->mpg_i, t2->mpg_i)
+ ) {
+ r = mpg_integer();
+ mpz_divexact(r->mpg_i, t1->mpg_i, t2->mpg_i);
+ } else {
+ mpfr_ptr p1, p2;
+ p1 = MP_FLOAT(t1);
+ p2 = MP_FLOAT(t2);
+ r = mpg_float();
+ tval = mpfr_div(r->mpg_numbr, p1, p2, RND_MODE);
+ IEEE_FMT(r->mpg_numbr, tval);
+ }
+ return r;
+}
+
+/* mpg_mod --- modulus operation with arbitrary-precision numbers */
+
+static NODE *
+mpg_mod(NODE *t1, NODE *t2)
+{
+ NODE *r;
+ int tval;
+
+ if (is_mpg_integer(t1) && is_mpg_integer(t2)) {
+ r = mpg_integer();
+ mpz_mod(r->mpg_i, t1->mpg_i, t2->mpg_i);
+ } else {
+ mpfr_ptr p1, p2;
+ p1 = MP_FLOAT(t1);
+ p2 = MP_FLOAT(t2);
+ r = mpg_float();
+ tval = mpfr_fmod(r->mpg_numbr, p1, p2, RND_MODE);
+ IEEE_FMT(r->mpg_numbr, tval);
+ }
+ return r;
+}
+
+/*
* mpg_interpret --- pre-exec hook in the interpreter. Handles
- * arithmetic operations with MPFR numbers.
+ * arithmetic operations with MPFR/GMP numbers.
*/
static int
@@ -864,9 +1360,7 @@ mpg_interpret(INSTRUCTION **cp)
NODE *r = NULL;
NODE *t1, *t2;
NODE **lhs;
- AWKNUM x;
- mpfr_ptr p1, p2;
- int tval;
+ int tval; /* the ternary value returned by a MPFR function */
switch ((op = pc->opcode)) {
case Op_plus_i:
@@ -876,9 +1370,7 @@ mpg_interpret(INSTRUCTION **cp)
t2 = POP_NUMBER();
plus:
t1 = TOP_NUMBER();
- r = mpg_node();
- tval = mpfr_add(r->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, RND_MODE);
- IEEE_FMT(r->mpg_numbr, tval);
+ r = mpg_add(t1, t2);
DEREF(t1);
if (op == Op_plus)
DEREF(t2);
@@ -892,9 +1384,7 @@ plus:
t2 = POP_NUMBER();
minus:
t1 = TOP_NUMBER();
- r = mpg_node();
- tval = mpfr_sub(r->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, RND_MODE);
- IEEE_FMT(r->mpg_numbr, tval);
+ r = mpg_sub(t1, t2);
DEREF(t1);
if (op == Op_minus)
DEREF(t2);
@@ -908,9 +1398,7 @@ minus:
t2 = POP_NUMBER();
times:
t1 = TOP_NUMBER();
- r = mpg_node();
- tval = mpfr_mul(r->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, RND_MODE);
- IEEE_FMT(r->mpg_numbr, tval);
+ r = mpg_mul(t1, t2);
DEREF(t1);
if (op == Op_times)
DEREF(t2);
@@ -924,9 +1412,7 @@ times:
t2 = POP_NUMBER();
exp:
t1 = TOP_NUMBER();
- r = mpg_node();
- tval = mpfr_pow(r->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, RND_MODE);
- IEEE_FMT(r->mpg_numbr, tval);
+ r = mpg_pow(t1, t2);
DEREF(t1);
if (op == Op_exp)
DEREF(t2);
@@ -940,9 +1426,7 @@ exp:
t2 = POP_NUMBER();
quotient:
t1 = TOP_NUMBER();
- r = mpg_node();
- tval = mpfr_div(r->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, RND_MODE);
- IEEE_FMT(r->mpg_numbr, tval);
+ r = mpg_div(t1, t2);
DEREF(t1);
if (op == Op_quotient)
DEREF(t2);
@@ -956,9 +1440,7 @@ quotient:
t2 = POP_NUMBER();
mod:
t1 = TOP_NUMBER();
- r = mpg_node();
- tval = mpfr_fmod(r->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, RND_MODE);
- IEEE_FMT(r->mpg_numbr, tval);
+ r = mpg_mod(t1, t2);
DEREF(t1);
if (op == Op_mod)
DEREF(t2);
@@ -967,56 +1449,84 @@ mod:
case Op_preincrement:
case Op_predecrement:
- x = op == Op_preincrement ? 1.0 : -1.0;
lhs = TOP_ADDRESS();
t1 = *lhs;
force_number(t1);
-#if 0
- /*
- * The optimizations for fixed precision do not always
- * work the same way in arbitrary precision. With this optimization on,
- * gawk -M 'BEGIN { PREC=53; i=2^53; PREC=113; ++i; print i}'
- * prints 2^53 instead of 2^53+1.
- */
+ if (is_mpg_integer(t1)) {
+ if (t1->valref == 1 && t1->flags == (MALLOC|MPZN|NUMCUR|NUMBER))
+ /* Efficiency hack. Big speed-up (> 30%) in a tight loop */
+ r = t1;
+ else
+ r = *lhs = mpg_integer();
+ if (op == Op_preincrement)
+ mpz_add_ui(r->mpg_i, t1->mpg_i, 1);
+ else
+ mpz_sub_ui(r->mpg_i, t1->mpg_i, 1);
+ } else {
- if (t1->valref == 1 && t1->flags == (MALLOC|MPFN|NUMCUR|NUMBER)) {
- /* optimization */
- tval = mpfr_add_d(t1->mpg_numbr, t1->mpg_numbr, x, RND_MODE);
- IEEE_FMT(t1->mpg_numbr, tval);
- r = t1;
+ /*
+ * An optimization like the one above is not going to work
+ * for a floating-point number. With it,
+ * gawk -M 'BEGIN { PREC=53; i=2^53+0.0; PREC=113; ++i; print i}'
+ * will output 2^53 instead of 2^53+1.
+ */
+
+ r = *lhs = mpg_float();
+ tval = mpfr_add_si(r->mpg_numbr, t1->mpg_numbr,
+ op == Op_preincrement ? 1 : -1,
+ RND_MODE);
+ IEEE_FMT(r->mpg_numbr, tval);
}
-#endif
- r = *lhs = mpg_node();
- tval = mpfr_add_d(r->mpg_numbr, t1->mpg_numbr, x, RND_MODE);
- IEEE_FMT(r->mpg_numbr, tval);
- unref(t1);
+ if (r != t1)
+ unref(t1);
UPREF(r);
REPLACE(r);
break;
case Op_postincrement:
case Op_postdecrement:
- x = op == Op_postincrement ? 1.0 : -1.0;
lhs = TOP_ADDRESS();
t1 = *lhs;
force_number(t1);
- r = mpg_node();
- tval = mpfr_set(r->mpg_numbr, t1->mpg_numbr, RND_MODE); /* r = t1 */
- IEEE_FMT(r->mpg_numbr, tval);
- t2 = *lhs = mpg_node();
- tval = mpfr_add_d(t2->mpg_numbr, t1->mpg_numbr, x, RND_MODE);
- IEEE_FMT(t2->mpg_numbr, tval);
- unref(t1);
+
+ if (is_mpg_integer(t1)) {
+ r = mpg_integer();
+ mpz_set(r->mpg_i, t1->mpg_i);
+ if (t1->valref == 1 && t1->flags == (MALLOC|MPZN|NUMCUR|NUMBER))
+ /* Efficiency hack. Big speed-up (> 30%) in a tight loop */
+ t2 = t1;
+ else
+ t2 = *lhs = mpg_integer();
+ if (op == Op_postincrement)
+ mpz_add_ui(t2->mpg_i, t1->mpg_i, 1);
+ else
+ mpz_sub_ui(t2->mpg_i, t1->mpg_i, 1);
+ } else {
+ r = mpg_float();
+ tval = mpfr_set(r->mpg_numbr, t1->mpg_numbr, RND_MODE);
+ IEEE_FMT(r->mpg_numbr, tval);
+ t2 = *lhs = mpg_float();
+ tval = mpfr_add_si(t2->mpg_numbr, t1->mpg_numbr,
+ op == Op_postincrement ? 1 : -1,
+ RND_MODE);
+ IEEE_FMT(t2->mpg_numbr, tval);
+ }
+ if (t2 != t1)
+ unref(t1);
REPLACE(r);
break;
case Op_unary_minus:
t1 = TOP_NUMBER();
- r = mpg_node();
- mpfr_set(r->mpg_numbr, t1->mpg_numbr, RND_MODE); /* r = t1 */
- tval = mpfr_neg(r->mpg_numbr, r->mpg_numbr, RND_MODE); /* change sign */
- IEEE_FMT(r->mpg_numbr, tval);
+ if (is_mpg_float(t1)) {
+ r = mpg_float();
+ tval = mpfr_neg(r->mpg_numbr, t1->mpg_numbr, RND_MODE);
+ IEEE_FMT(r->mpg_numbr, tval);
+ } else {
+ r = mpg_integer();
+ mpz_neg(r->mpg_i, t1->mpg_i);
+ }
DEREF(t1);
REPLACE(r);
break;
@@ -1029,41 +1539,35 @@ mod:
case Op_assign_exp:
lhs = POP_ADDRESS();
t1 = *lhs;
- p1 = force_number(t1)->mpg_numbr;
-
+ force_number(t1);
t2 = TOP_NUMBER();
- p2 = t2->mpg_numbr;
- r = mpg_node();
switch (op) {
case Op_assign_plus:
- tval = mpfr_add(r->mpg_numbr, p1, p2, RND_MODE);
+ r = mpg_add(t1, t2);
break;
case Op_assign_minus:
- tval = mpfr_sub(r->mpg_numbr, p1, p2, RND_MODE);
+ r = mpg_sub(t1, t2);
break;
case Op_assign_times:
- tval = mpfr_mul(r->mpg_numbr, p1, p2, RND_MODE);
+ r = mpg_mul(t1, t2);
break;
case Op_assign_quotient:
- tval = mpfr_div(r->mpg_numbr, p1, p2, RND_MODE);
+ r = mpg_div(t1, t2);
break;
case Op_assign_mod:
- tval = mpfr_fmod(r->mpg_numbr, p1, p2, RND_MODE);
+ r = mpg_mod(t1, t2);
break;
case Op_assign_exp:
- tval = mpfr_pow(r->mpg_numbr, p1, p2, RND_MODE);
+ r = mpg_pow(t1, t2);
break;
default:
cant_happen();
}
- IEEE_FMT(r->mpg_numbr, tval);
-
DEREF(t2);
unref(*lhs);
*lhs = r;
-
UPREF(r);
REPLACE(r);
break;