summaryrefslogtreecommitdiffstats
path: root/arith.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2011-12-11 19:56:03 -0800
committerKaz Kylheku <kaz@kylheku.com>2011-12-11 19:56:03 -0800
commit68fbc5322e282f41e2ee8c84cc16f6b6a4d39670 (patch)
tree8a8f3a52a29e05d9267d45b7774288450aaffee4 /arith.c
parentaf4986ea5e82d32f9699d41781f60d9b77ba9748 (diff)
downloadtxr-68fbc5322e282f41e2ee8c84cc16f6b6a4d39670.tar.gz
txr-68fbc5322e282f41e2ee8c84cc16f6b6a4d39670.tar.bz2
txr-68fbc5322e282f41e2ee8c84cc16f6b6a4d39670.zip
Bignum division implemented. More portability bugs found in MPI:
code like 1 << n, where n exceeds the width of the type int. * arith.c (trunc): New function, reimplementation of removed trunc from lib.c. * lib.c (trunc): Removed. * mpi-patches/fix-bad-shifts: New file.
Diffstat (limited to 'arith.c')
-rw-r--r--arith.c61
1 files changed, 61 insertions, 0 deletions
diff --git a/arith.c b/arith.c
index f87c5693..89907332 100644
--- a/arith.c
+++ b/arith.c
@@ -497,6 +497,67 @@ val mul(val anum, val bnum)
abort();
}
+val trunc(val anum, val bnum)
+{
+ int tag_a = tag(anum);
+ int tag_b = tag(bnum);
+
+ switch (TAG_PAIR(tag_a, tag_b)) {
+ case TAG_PAIR(TAG_NUM, TAG_NUM):
+ {
+ cnum a = c_num(anum);
+ cnum b = c_num(bnum);
+ cnum ap = ABS(a);
+ cnum bp = ABS(b);
+ int neg = ((a < 0 && b > 0) || (a > 0 && b < 0));
+
+ if (b == 0)
+ uw_throw(numeric_error_s, lit("division by zero"));
+
+ {
+ cnum quot = ap / bp;
+ return num(neg ? -quot : quot);
+ }
+ }
+ case TAG_PAIR(TAG_NUM, TAG_PTR):
+ type_check(bnum, BGNUM);
+ return zero;
+ case TAG_PAIR(TAG_PTR, TAG_NUM):
+ {
+ val n;
+ type_check(anum, BGNUM);
+ n = make_bignum();
+ if (sizeof (int_ptr_t) <= sizeof (mp_digit)) {
+ cnum b = c_num(bnum);
+ cnum bp = ABS(b);
+ if (mp_div_d(mp(anum), bp, mp(n), 0) != MP_OKAY)
+ uw_throw(numeric_error_s, lit("division by zero"));
+ if (b < 0)
+ mp_neg(mp(n), mp(n));
+ } else {
+ mp_int tmp;
+ mp_init(&tmp);
+ mp_set_intptr(&tmp, c_num(bnum));
+ if (mp_div(mp(anum), &tmp, mp(n), 0) != MP_OKAY)
+ uw_throw(numeric_error_s, lit("division by zero"));
+ }
+ return normalize(n);
+ }
+ case TAG_PAIR(TAG_PTR, TAG_PTR):
+ {
+ val n;
+ type_check(anum, BGNUM);
+ type_check(bnum, BGNUM);
+ n = make_bignum();
+ if (mp_div(mp(anum), mp(bnum), mp(n), 0) != MP_OKAY)
+ uw_throw(numeric_error_s, lit("division by zero"));
+ return normalize(n);
+ }
+ }
+ uw_throwf(error_s, lit("trunc: invalid operands ~s ~s"), anum, bnum, nao);
+ abort();
+}
+
void arith_init(void)
{
mp_init(&NUM_MAX_MP);