diff options
author | mame <mame@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2010-04-20 15:31:46 +0000 |
---|---|---|
committer | mame <mame@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2010-04-20 15:31:46 +0000 |
commit | 52998fa5918fdf64369ce47773c895b78032b7cd (patch) | |
tree | dddcf45b5e868703cece6a4514e6a83107cc53ee /bignum.c | |
parent | adfe4f39304f330cebe673d603b226713ce58210 (diff) | |
download | ruby-52998fa5918fdf64369ce47773c895b78032b7cd.tar.gz |
* bignum.c (bigmul1_karatsuba): fix calculation order to prevent
underflow. [ruby-core:29088]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27425 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'bignum.c')
-rw-r--r-- | bignum.c | 33 |
1 files changed, 18 insertions, 15 deletions
@@ -2077,7 +2077,7 @@ static VALUE bigmul1_karatsuba(VALUE x, VALUE y) { long i, n, xn, yn, t1n, t2n; - VALUE xh, xl, yh, yl, z, t1, t2; + VALUE xh, xl, yh, yl, z, t1, t2, t3; BDIGIT *zds; xn = RBIGNUM_LEN(x); @@ -2122,24 +2122,19 @@ bigmul1_karatsuba(VALUE x, VALUE y) /* copy t2 into low bytes of the result (z0) */ MEMCPY(zds, BDIGITS(t2), BDIGIT, t2n); for (i = t2n; i < 2 * n; i++) zds[i] = 0; - - /* subtract t2 from middle bytes of the result (z1) */ - i = xn + yn - n; - bigsub_core(zds + n, i, BDIGITS(t2), t2n, zds + n, i); } else { + t2 = Qundef; + /* copy 0 into low bytes of the result (z0) */ for (i = 0; i < 2 * n; i++) zds[i] = 0; } - /* subtract t1 from middle bytes of the result (z1) */ - i = xn + yn - n; - bigsub_core(zds + n, i, BDIGITS(t1), t1n, zds + n, i); - /* xh <- xh + xl */ if (RBIGNUM_LEN(xl) > RBIGNUM_LEN(xh)) { - t1 = xl; xl = xh; xh = t1; + t3 = xl; xl = xh; xh = t3; } + /* xh has a margin for carry */ bigadd_core(BDIGITS(xh), RBIGNUM_LEN(xh), BDIGITS(xl), RBIGNUM_LEN(xl), BDIGITS(xh), RBIGNUM_LEN(xh)); @@ -2147,19 +2142,27 @@ bigmul1_karatsuba(VALUE x, VALUE y) /* yh <- yh + yl */ if (x != y) { if (RBIGNUM_LEN(yl) > RBIGNUM_LEN(yh)) { - t1 = yl; yl = yh; yh = t1; + t3 = yl; yl = yh; yh = t3; } + /* yh has a margin for carry */ bigadd_core(BDIGITS(yh), RBIGNUM_LEN(yh), BDIGITS(yl), RBIGNUM_LEN(yl), BDIGITS(yh), RBIGNUM_LEN(yh)); } else yh = xh; - /* t1 <- xh * yh */ - t1 = bigmul0(xh, yh); + /* t3 <- xh * yh */ + t3 = bigmul0(xh, yh); + + i = xn + yn - n; + /* add t3 to middle bytes of the result (z1) */ + bigadd_core(zds + n, i, BDIGITS(t3), big_real_len(t3), zds + n, i); + + /* subtract t1 from middle bytes of the result (z1) */ + bigsub_core(zds + n, i, BDIGITS(t1), t1n, zds + n, i); - /* add t1 to middle bytes of the result (z1) */ - bigadd_core(zds + n, i, BDIGITS(t1), big_real_len(t1), zds + n, i); + /* subtract t2 from middle bytes of the result (z1) */ + if (t2 != Qundef) bigsub_core(zds + n, i, BDIGITS(t2), t2n, zds + n, i); return z; } |