>From d3b7e9c346aaa517aa27c22b17238a57e11fc84d Mon Sep 17 00:00:00 2001 From: Mark H Weaver Date: Wed, 26 Jan 2011 05:21:03 -0500 Subject: [PATCH] Fix bugs when negating SCM_MOST_POSITIVE_FIXNUM+1 * libguile/numbers.c (scm_difference, scm_product): Fix bugs when negating SCM_MOST_POSITIVE_FIXNUM+1, aka -SCM_MOST_NEGATIVE_FIXNUM. Previously, these cases failed to normalize the result to a fixnum, causing `=', `eqv?' and `equal?' to fail, e.g.: (= most-negative-fixnum (- 0 (- most-negative-fixnum))) (= most-negative-fixnum (* -1 (- most-negative-fixnum))) (= most-negative-fixnum (* (- most-negative-fixnum) -1)) * test-suite/test/numbers.test: Add test cases to detect bugs when negating SCM_MOST_POSITIVE_FIXNUM+1 and SCM_MOST_NEGATIVE_FIXNUM by various methods. --- libguile/numbers.c | 17 ++++++++++++++++- test-suite/tests/numbers.test | 27 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletions(-) diff --git a/libguile/numbers.c b/libguile/numbers.c index c6a2162..bfa6c22 100644 --- a/libguile/numbers.c +++ b/libguile/numbers.c @@ -4465,7 +4465,11 @@ scm_difference (SCM x, SCM y) scm_t_inum xx = SCM_I_INUM (x); if (xx == 0) - return scm_i_clonebig (y, 0); + { + /* Must scm_i_normbig here because -SCM_MOST_NEGATIVE_FIXNUM is a + bignum, but negating that gives a fixnum. */ + return scm_i_normbig (scm_i_clonebig (y, 0)); + } else { int sgn_y = mpz_sgn (SCM_I_BIG_MPZ (y)); @@ -4697,6 +4701,17 @@ scm_product (SCM x, SCM y) { case 0: return x; break; case 1: return y; break; + /* + * The following case (x = -1) is important for more than + * just optimization. It handles the case of negating + * (+ 1 most-positive-fixnum) aka (- most-negative-fixnum), + * which is a bignum that must be changed back into a fixnum. + * Failure to do so will cause the following to return #f: + * (= most-negative-fixnum (* -1 (- most-negative-fixnum))) + */ + case -1: + return scm_difference(y, SCM_UNDEFINED); + break; } if (SCM_LIKELY (SCM_I_INUMP (y))) diff --git a/test-suite/tests/numbers.test b/test-suite/tests/numbers.test index 7b0b73f..2d20ef2 100644 --- a/test-suite/tests/numbers.test +++ b/test-suite/tests/numbers.test @@ -2597,6 +2597,20 @@ (with-test-prefix/c&e "-" + (pass-if "double-negation of fixnum-min: =" + (= fixnum-min (- (- fixnum-min)))) + (pass-if "double-negation of fixnum-min: eqv?" + (eqv? fixnum-min (- (- fixnum-min)))) + (pass-if "double-negation of fixnum-min: equal?" + (equal? fixnum-min (- (- fixnum-min)))) + + (pass-if "binary double-negation of fixnum-min: =" + (= fixnum-min (- 0 (- 0 fixnum-min)))) + (pass-if "binary double-negation of fixnum-min: eqv?" + (eqv? fixnum-min (- 0 (- 0 fixnum-min)))) + (pass-if "binary double-negation of fixnum-min: equal?" + (equal? fixnum-min (- 0 (- 0 fixnum-min)))) + (pass-if "-inum - +bignum" (= #x-100000000000000000000000000000001 (- -1 #x100000000000000000000000000000000))) @@ -2626,6 +2640,14 @@ (with-test-prefix "*" + (with-test-prefix "double-negation of fixnum-min" + (pass-if (= fixnum-min (* -1 (* -1 fixnum-min)))) + (pass-if (eqv? fixnum-min (* -1 (* -1 fixnum-min)))) + (pass-if (equal? fixnum-min (* -1 (* -1 fixnum-min)))) + (pass-if (= fixnum-min (* (* fixnum-min -1) -1))) + (pass-if (eqv? fixnum-min (* (* fixnum-min -1) -1))) + (pass-if (equal? fixnum-min (* (* fixnum-min -1) -1)))) + (with-test-prefix "inum * bignum" (pass-if "0 * 2^256 = 0" @@ -2679,6 +2701,11 @@ (with-test-prefix "/" + (with-test-prefix "double-negation of fixnum-min" + (pass-if (= fixnum-min (/ (/ fixnum-min -1) -1))) + (pass-if (eqv? fixnum-min (/ (/ fixnum-min -1) -1))) + (pass-if (equal? fixnum-min (/ (/ fixnum-min -1) -1)))) + (pass-if "documented?" (documented? /)) -- 1.5.6.5