Do not lose carry bit in addition edge case

Previous code was losing the carry bit in 'all ones' case, when adata[i]
= bdata[i] = SEXP_UINT_T_MAX, and carry = 1 too. In this case expression
(SEXP_UINT_T_MAX - bdata[i] - carry) overflows and yields an incorrect
value SEXP_UINT_T_MAX which results into carry being incorrectly set to
0 after addition.

We need to avoid the second overflow when calculating the new value of
the carry bit. One way to do this is at first check for the overflow in
(adata[i] + bdata[i]), and then throw in the (previous) carry bit.

I have also given "n" more expressive name and added a comment about
the reason why we need that temporary variable.
This commit is contained in:
ilammy 2015-03-25 03:29:12 +02:00
parent a1ec8ff493
commit 8329ee9fd6
2 changed files with 17 additions and 4 deletions

View file

@ -367,7 +367,7 @@ sexp sexp_bignum_sub_digits (sexp ctx, sexp dst, sexp a, sexp b) {
sexp sexp_bignum_add_digits (sexp ctx, sexp dst, sexp a, sexp b) {
sexp_uint_t alen=sexp_bignum_hi(a), blen=sexp_bignum_hi(b),
carry=0, i, n, *adata, *bdata, *cdata;
carry=0, i, old_a, p_sum, *adata, *bdata, *cdata;
sexp_gc_var1(c);
if (alen < blen) return sexp_bignum_add_digits(ctx, dst, b, a);
sexp_gc_preserve1(ctx, c);
@ -377,9 +377,11 @@ sexp sexp_bignum_add_digits (sexp ctx, sexp dst, sexp a, sexp b) {
bdata = sexp_bignum_data(b);
cdata = sexp_bignum_data(c);
for (i=0; i<blen; i++) {
n = adata[i];
cdata[i] = n + bdata[i] + carry;
carry = (n > (SEXP_UINT_T_MAX - bdata[i] - carry) ? 1 : 0);
old_a = adata[i]; /* adata may alias cdata */
p_sum = adata[i] + bdata[i];
cdata[i] = p_sum + carry;
carry = (old_a > (SEXP_UINT_T_MAX - bdata[i]) ? 1 : 0)
+ (p_sum > (SEXP_UINT_T_MAX - carry) ? 1 : 0);
}
for ( ; carry && (i<alen); i++) {
carry = (cdata[i] == SEXP_UINT_T_MAX ? 1 : 0);

View file

@ -205,6 +205,17 @@
98079714615416886934934209737619787751599303819750539264 18446744073709551616 0))
(sign-combinations (expt 2 125) (expt 2 61)))
;; Regression tests for an overflow in bignum addition
(test 8589869056
(+ 4294934528 4294934528))
(test 36893488143124135936
(+ 18446744071562067968 18446744071562067968))
(test 680564733841876926908302470789826871296
(+ 340282366920938463454151235394913435648 340282366920938463454151235394913435648))
(test 231584178474632390847141970017375815706199686964360189615451793408394491068416
(+ 115792089237316195423570985008687907853099843482180094807725896704197245534208
115792089237316195423570985008687907853099843482180094807725896704197245534208))
(test #f (< +nan.0 +nan.0))
(test #f (<= +nan.0 +nan.0))
(test #f (= +nan.0 +nan.0))