[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [bug-gawk] GAWK 4.1: Division with Arbitrary Precision Integers
From: |
Aharon Robbins |
Subject: |
Re: [bug-gawk] GAWK 4.1: Division with Arbitrary Precision Integers |
Date: |
Wed, 09 Jul 2014 17:27:54 -0700 |
User-agent: |
Heirloom mailx 12.5 6/20/10 |
Hello all.
It's taken a little while. I have something that does
div(numerator, denominator, result)
filling in "quotient" and "remainder" in result. I belive it works
correctly for doubles and MPFR / GMP ints. The semantics are
numerator = int(numerator)
denominator = int(denonimator)
quotient = int(numerator / denominator)
remainder = int(numerator % denominator)
Note that in the case of a / b where a is evenly divisible by b, that
gawk already does division using GMP integers.
This has been an interesting learning experience. :-)
This diff is relative to the master branch in the git repo. Please
review and let me know what you think.
Thanks,
Arnold
----------------------------
diff --git a/awk.h b/awk.h
index 9255b45..cb9f809 100644
--- a/awk.h
+++ b/awk.h
@@ -1419,6 +1419,7 @@ extern AWKNUM nondec2awknum(char *str, size_t len);
extern NODE *do_dcgettext(int nargs);
extern NODE *do_dcngettext(int nargs);
extern NODE *do_bindtextdomain(int nargs);
+extern NODE *do_div(int nargs);
#if MBS_SUPPORT
extern int strncasecmpmbs(const unsigned char *,
const unsigned char *, size_t);
@@ -1555,6 +1556,7 @@ extern NODE *do_mpfr_and(int);
extern NODE *do_mpfr_atan2(int);
extern NODE *do_mpfr_compl(int);
extern NODE *do_mpfr_cos(int);
+extern NODE *do_mpfr_div(int);
extern NODE *do_mpfr_exp(int);
extern NODE *do_mpfr_int(int);
extern NODE *do_mpfr_log(int);
diff --git a/awkgram.y b/awkgram.y
index b512584..700334e 100644
--- a/awkgram.y
+++ b/awkgram.y
@@ -1860,6 +1860,7 @@ static const struct token tokentab[] = {
{"dcngettext", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3)|A(4)|A(5),
do_dcngettext, 0},
{"default", Op_K_default, LEX_DEFAULT, GAWKX, 0, 0},
{"delete", Op_K_delete, LEX_DELETE, NOT_OLD, 0, 0},
+{"div", Op_builtin, LEX_BUILTIN, GAWKX|A(3), do_div,
MPF(div)},
{"do", Op_K_do, LEX_DO, NOT_OLD|BREAK|CONTINUE, 0,
0},
{"else", Op_K_else, LEX_ELSE, 0, 0, 0},
{"eval", Op_symbol, LEX_EVAL, 0, 0, 0},
@@ -3877,7 +3878,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r)
}
#ifdef HAVE_MPFR
- /* N.B.: There isn't any special processing for an alternate function
below */
+ /* N.B.: If necessary, add special processing for alternate builtin,
below */
if (do_mpfr && tokentab[idx].ptr2)
r->builtin = tokentab[idx].ptr2;
else
@@ -3906,6 +3907,15 @@ snode(INSTRUCTION *subn, INSTRUCTION *r)
arg = subn->nexti;
if (arg->nexti == arg->lasti && arg->nexti->opcode == Op_push)
arg->nexti->opcode = Op_push_arg; /* argument may
be array */
+ } else if (r->builtin == do_div
+#ifdef HAVE_MPFR
+ || r->builtin == MPF(div)
+#endif
+ ) {
+ arg = subn->nexti->lasti->nexti->lasti->nexti; /* 3rd arg list
*/
+ ip = arg->lasti;
+ if (ip->opcode == Op_push)
+ ip->opcode = Op_push_array;
} else if (r->builtin == do_match) {
static bool warned = false;
diff --git a/builtin.c b/builtin.c
index 54f5235..717fe5c 100644
--- a/builtin.c
+++ b/builtin.c
@@ -3612,6 +3612,72 @@ do_bindtextdomain(int nargs)
return make_string(the_result, strlen(the_result));
}
+/* do_div --- 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_div(int nargs)
+{
+ NODE *numerator, *denominator, *result;
+ double num, denom, quotient, remainder;
+ NODE *sub, **lhs;
+
+ result = POP_PARAM();
+ if (result->type != Node_var_array)
+ fatal(_("div: third argument is not an array"));
+ assoc_clear(result);
+
+ denominator = POP_SCALAR();
+ numerator = POP_SCALAR();
+
+ if (do_lint) {
+ if ((numerator->flags & (NUMCUR|NUMBER)) == 0)
+ lintwarn(_("div: received non-numeric first argument"));
+ if ((denominator->flags & (NUMCUR|NUMBER)) == 0)
+ lintwarn(_("div: received non-numeric second
argument"));
+ }
+
+ (void) force_number(numerator);
+ (void) force_number(denominator);
+ num = double_to_int(get_number_d(numerator));
+ denom = double_to_int(get_number_d(denominator));
+
+ if (denom == 0.0)
+ fatal(_("div: division by zero attempted"));
+
+ quotient = double_to_int(num / denom);
+ /*
+ * FIXME: This code is duplicated, factor it out to a
+ * separate function.
+ */
+#ifdef HAVE_FMOD
+ remainder = fmod(num, denom);
+#else /* ! HAVE_FMOD */
+ (void) modf(num / denom, & remainder);
+ remainder = num - remainder * denom;
+#endif /* ! HAVE_FMOD */
+ remainder = double_to_int(remainder);
+
+ sub = make_string("quotient", 8);
+ lhs = assoc_lookup(result, sub);
+ unref(*lhs);
+ *lhs = make_number((AWKNUM) quotient);
+
+ sub = make_string("remainder", 9);
+ lhs = assoc_lookup(result, sub);
+ unref(*lhs);
+ *lhs = make_number((AWKNUM) remainder);
+
+ return make_number((AWKNUM) 0.0);
+}
+
/* mbc_byte_count --- return number of bytes for corresponding numchars
multibyte characters */
diff --git a/mpfr.c b/mpfr.c
index 393a2b1..e9c9d15 100644
--- a/mpfr.c
+++ b/mpfr.c
@@ -1166,6 +1166,93 @@ do_mpfr_srand(int nargs)
return res;
}
+/* do_mpfr_div --- 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_div(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(_("div: third argument is not an array"));
+ assoc_clear(result);
+
+ denominator = POP_SCALAR();
+ numerator = POP_SCALAR();
+
+ if (do_lint) {
+ if ((numerator->flags & (NUMCUR|NUMBER)) == 0)
+ lintwarn(_("div: received non-numeric first argument"));
+ if ((denominator->flags & (NUMCUR|NUMBER)) == 0)
+ lintwarn(_("div: 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(_("div: 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);
+
+ 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
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- Re: [bug-gawk] GAWK 4.1: Division with Arbitrary Precision Integers,
Aharon Robbins <=