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