All --

As I understand it, computer languages differ in their implementation
of the mod() function. I've heard some architectures differ, too, so
that the same language (such as C or Perl) might behave differently on
different machines (I'd be interested to know if this is really true).

Fortunately, there is a perfectly rational definition of the mod(x,y)
function over the full domains of x and y, and even for x and y that
are not integral. This can be found in the book Concrete Mathematics,
Second Edition by Graham, Knuth and Patashnik (Section 3.4, pages
81-85.

It seems to me that if we are going to offer folks a 'mod' operator,
we should be (a) guaranteeing that wherever their program is running,
they'll get the same behavior, even with negative arguments and
(b) picking among the possible definitions the one with the most
complete coverage of the domain and most 'useful' mathematical
definition.

With that in mind, I've implemented the GKP mod function for Parrot,
and added tests for it to t/op/integer.t and t/op/number.t. Just in
case someone needs to use the underlying C mod function (the % operator
for integers, and the fmod() function from math.h for numbers), I've
also implemented cmod_i and cmod_n ops.

This way, if someone is doing something with 'mod' in the inner loop,
and knows that the underlying C version behaves as they want for all
possible argument values the inner loop will use, they can use the
faster version.

Anyway, here's the patch if anyone wants to play with it. Of course,
this is at best post-0.0.2 material, and at worst what-the-hell-were-
you-thinking-gregor material. :-)

BTW, one application for which I know these versions of mod to be
useful are in Calendrical Calculations, at least as discussed in
the book of that name.


Regards,

-- Gregor
 _____________________________________________________________________ 
/     perl -e 'srand(-2091643526); print chr rand 90 for (0..4)'      \

   Gregor N. Purdy                          [EMAIL PROTECTED]
   Focus Research, Inc.                http://www.focusresearch.com/
   8080 Beckett Center Drive #203                   513-860-3570 vox
   West Chester, OH 45069                           513-860-3579 fax
\_____________________________________________________________________/
Index: basic_opcodes.ops
===================================================================
RCS file: /home/perlcvs/parrot/basic_opcodes.ops,v
retrieving revision 1.24
diff -u -r1.24 basic_opcodes.ops
--- basic_opcodes.ops   2001/09/24 16:27:48     1.24
+++ basic_opcodes.ops   2001/09/24 18:12:02
@@ -7,6 +7,13 @@
 #include "parrot/parrot.h"
 #include <math.h>
 
+/* DEFINES to make true mod work right */
+
+#define OPP_SIGNS(x,y)     ((((x)<0)&&((y)>0))||(((x)>0)&&((y)<0)))
+#define REM_INC(x,y)       (((x)%(y))?1:0)
+#define INT_FLOOR_DIV(x,y) (OPP_SIGNS(x,y)?(-((-x)/(y)+REM_INC((-x),(y)))):((x)/(y)))
+#define INT_MOD(x,y)       (((y)==0)?(x):((x)-(y)*INT_FLOOR_DIV((x),(y))))
+
 /* SET Ix, CONSTANT */
 AUTO_OP set_i_ic {
   INT_REG(P1) = P2;
@@ -43,8 +50,12 @@
 
 /* MOD Ix, Iy, Iz   */
 AUTO_OP mod_i {
-  INT_REG(P1) = INT_REG(P2) %
-                           INT_REG(P3);
+  INT_REG(P1) = INT_MOD(INT_REG(P2), INT_REG(P3));
+}
+
+/* CMOD Ix, Iy, Iz   */
+AUTO_OP cmod_i {
+  INT_REG(P1) = INT_REG(P2) % INT_REG(P3);
 }
 
 /* EQ Ix, Iy, EQ_BRANCH */
@@ -221,6 +232,18 @@
 AUTO_OP div_n {
   NUM_REG(P1) = NUM_REG(P2) /
                            NUM_REG(P3);
+}
+
+/* MOD Nx, Ny, Nz   */
+AUTO_OP mod_n {
+  NUM_REG(P1) = NUM_REG(P3)
+    ? (NUM_REG(P2) - NUM_REG(P3) * floor(NUM_REG(P2) / NUM_REG(P3)))
+    : NUM_REG(P2);
+}
+
+/* CMOD Nx, Ny, Nz   */
+AUTO_OP cmod_n {
+  NUM_REG(P1) = fmod(NUM_REG(P2), NUM_REG(P3));
 }
 
 /* EQ Nx, Ny, EQ_BRANCH */
Index: opcode_table
===================================================================
RCS file: /home/perlcvs/parrot/opcode_table,v
retrieving revision 1.22
diff -u -r1.22 opcode_table
--- opcode_table        2001/09/24 16:27:48     1.22
+++ opcode_table        2001/09/24 18:12:02
@@ -36,6 +36,7 @@
 mul_i  3       I I I
 div_i  3       I I I
 mod_i  3       I I I
+cmod_i 3       I I I
 inc_i  1       I
 inc_i_ic       2       I i
 dec_i  1       I
@@ -49,6 +50,8 @@
 sub_n  3       N N N
 mul_n  3       N N N
 div_n  3       N N N
+mod_n  3       N N N
+cmod_n 3       N N N
 inc_n  1       N
 inc_n_nc       2       N n
 dec_n  1       N
Index: t/op/integer.t
===================================================================
RCS file: /home/perlcvs/parrot/t/op/integer.t,v
retrieving revision 1.5
diff -u -r1.5 integer.t
--- t/op/integer.t      2001/09/20 14:03:59     1.5
+++ t/op/integer.t      2001/09/24 18:12:02
@@ -1,6 +1,6 @@
 #! perl -w
 
-use Parrot::Test tests => 26;
+use Parrot::Test tests => 27;
 
 output_is(<<CODE, <<OUTPUT, "set_i_ic");
        # XXX: Need a test for writing outside the set of available
@@ -218,21 +218,55 @@
 OUTPUT
 
 output_is(<<CODE, <<OUTPUT, "mod_i");
-       set     I0, 17
-       set     I1, 5
+       set     I0, 5
+       set     I1, 0
        mod     I2, I0, I1
        print   I2
        print   "\\n"
 
-       set     I0, -57
-       set     I1, 10
+       set     I0, 5
+       set     I1, 3
        mod     I2, I0, I1
        print   I2
        print   "\\n"
+
+       set     I0, 5
+       set     I1, -3
+       mod     I2, I0, I1
+       print   I2
+       print   "\\n"
+
+       set     I0, -5
+       set     I1, 3
+       mod     I2, I0, I1
+       print   I2
+       print   "\\n"
+
+       set     I0, -5
+       set     I1, -3
+       mod     I2, I0, I1
+       print   I2
+       print   "\\n"
+
+        end
+CODE
+5
+2
+-1
+1
+-2
+OUTPUT
+
+output_is(<<CODE, <<OUTPUT, "cmod_i");
+       set     I0, 5
+       set     I1, 3
+       mod     I2, I0, I1
+       print   I2
+       print   "\\n"
+
         end
 CODE
 2
--7
 OUTPUT
 
 output_is(<<CODE, <<OUTPUT, "eq_i_ic");
Index: t/op/number.t
===================================================================
RCS file: /home/perlcvs/parrot/t/op/number.t,v
retrieving revision 1.7
diff -u -r1.7 number.t
--- t/op/number.t       2001/09/24 18:08:50     1.7
+++ t/op/number.t       2001/09/24 18:12:02
@@ -1,6 +1,6 @@
 #! perl -w
 
-use Parrot::Test tests => 25;
+use Parrot::Test tests => 27;
 
 output_is(<<CODE, <<OUTPUT, "set_n_nc");
        set     N0, 1.0
@@ -194,6 +194,58 @@
 -2.250000
 OUTPUT
 
+output_is(<<CODE, <<OUTPUT, "mod_n");
+       set     N0, 5.0
+       set     N1, 0.0
+       mod     N2, N0, N1
+       print   N2
+       print   "\\n"
+
+       set     N0, 5.0
+       set     N1, 3.0
+       mod     N2, N0, N1
+       print   N2
+       print   "\\n"
+
+       set     N0, 5.0
+       set     N1, -3.0
+       mod     N2, N0, N1
+       print   N2
+       print   "\\n"
+
+       set     N0, -5.0
+       set     N1, 3.0
+       mod     N2, N0, N1
+       print   N2
+       print   "\\n"
+
+       set     N0, -5.0
+       set     N1, -3.0
+       mod     N2, N0, N1
+       print   N2
+       print   "\\n"
+
+        end
+CODE
+5.000000
+2.000000
+-1.000000
+1.000000
+-2.000000
+OUTPUT
+
+output_is(<<CODE, <<OUTPUT, "cmod_n");
+       set     N0, 5.000
+       set     N1, 3.000
+       cmod    N2, N0, N1
+       print   N2
+       print   "\\n"
+
+        end
+CODE
+2.000000
+OUTPUT
+
 output_is(<<CODE, <<OUTPUT, "eq_n_ic");
        set     N0, 5.000001
        set     N1, 5.000001
@@ -679,7 +731,6 @@
 -0.500000
 0.500000
 OUTPUT
-
 
 output_is(<<CODE, <<OUTPUT, "ntoi_i_n");
        set     N0, 0.0

Reply via email to