bug-gawk
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]