[Gmp-commit] /var/hg/gmp: Fix arithmetic overflow bug in mpn_invert_3by2
mercurial at gmplib.org
mercurial at gmplib.org
Wed Nov 16 22:07:26 UTC 2016
details: /var/hg/gmp/rev/e7a52acedab7
changeset: 17107:e7a52acedab7
user: Niels M?ller <nisse at lysator.liu.se>
date: Wed Nov 16 23:07:02 2016 +0100
description:
Fix arithmetic overflow bug in mpn_invert_3by2
And improve documentation of the algorithm.
diffstat:
mini-gmp/ChangeLog | 5 +++++
mini-gmp/mini-gmp.c | 50 +++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 46 insertions(+), 9 deletions(-)
diffs (98 lines):
diff -r b633a167ce0d -r e7a52acedab7 mini-gmp/ChangeLog
--- a/mini-gmp/ChangeLog Tue Nov 15 19:17:51 2016 +0100
+++ b/mini-gmp/ChangeLog Wed Nov 16 23:07:02 2016 +0100
@@ -1,3 +1,8 @@
+2016-11-16 Niels Möller <nisse at lysator.liu.se>
+
+ * mini-gmp/mini-gmp.c (mpn_invert_3by2): Fix arithmetic overflow
+ bug, and improve documentation of the algorithm.
+
2016-11-15 Niels Möller <nisse at lysator.liu.se>
* mini-gmp/tests/t-limbs.c (testmain): Skip tests with zero
diff -r b633a167ce0d -r e7a52acedab7 mini-gmp/mini-gmp.c
--- a/mini-gmp/mini-gmp.c Tue Nov 15 19:17:51 2016 +0100
+++ b/mini-gmp/mini-gmp.c Wed Nov 16 23:07:02 2016 +0100
@@ -725,22 +725,44 @@
/* MPN division interface. */
+
+/* The 3/2 inverse is defined as
+
+ m = floor( (B^3-1) / (B u1 + u0)) - B
+*/
mp_limb_t
mpn_invert_3by2 (mp_limb_t u1, mp_limb_t u0)
{
- mp_limb_t r, p, m;
- unsigned ul, uh;
- unsigned ql, qh;
-
- /* First, do a 2/1 inverse. */
- /* The inverse m is defined as floor( (B^2 - 1 - u1)/u1 ), so that 0 <
- * B^2 - (B + m) u1 <= u1 */
+ mp_limb_t r, p, m, ql;
+ unsigned ul, uh, qh;
+
assert (u1 >= GMP_LIMB_HIGHBIT);
+ /* For notation, let b denote the half-limb base, so that B = b^2.
+ Split u1 = b uh + ul. */
ul = u1 & GMP_LLIMB_MASK;
uh = u1 >> (GMP_LIMB_BITS / 2);
+ /* Approximation of the high half of quotient. Differs from the 2/1
+ inverse of the half limb uh, since we have already subtracted
+ u0. */
qh = ~u1 / uh;
+
+ /* Adjust to get a half-limb 3/2 inverse, i.e., we want
+
+ qh' = floor( (b^3 - 1) / u) - b = floor ((b^3 - b u - 1) / u
+ = floor( (b (~u) + b-1) / u),
+
+ and the remainder
+
+ r = b (~u) + b-1 - qh (b uh + ul)
+ = b (~u - qh uh) + b-1 - qh ul
+
+ Subtraction of qh ul may underflow, which implies adjustments.
+ But by normalization, 2 u >= B > qh ul, so we need to adjust by
+ at most 2.
+ */
+
r = ((~u1 - (mp_limb_t) qh * uh) << (GMP_LIMB_BITS / 2)) | GMP_LLIMB_MASK;
p = (mp_limb_t) qh * ul;
@@ -758,11 +780,19 @@
}
r -= p;
- /* Do a 3/2 division (with half limb size) */
+ /* Low half of the quotient is
+
+ ql = floor ( (b r + b-1) / u1).
+
+ This is a 3/2 division (on half-limbs), for which qh is a
+ suitable inverse. */
+
p = (r >> (GMP_LIMB_BITS / 2)) * qh + r;
+ /* Unlike full-limb 3/2, we can add 1 without overflow. For this to
+ work, it is essential that ql is a full mp_limb_t. */
ql = (p >> (GMP_LIMB_BITS / 2)) + 1;
- /* By the 3/2 method, we don't need the high half limb. */
+ /* By the 3/2 trick, we don't need the high half limb. */
r = (r << (GMP_LIMB_BITS / 2)) + GMP_LLIMB_MASK - ql * u1;
if (r >= (p << (GMP_LIMB_BITS / 2)))
@@ -777,6 +807,8 @@
r -= u1;
}
+ /* Now m is the 2/1 invers of u1. If u0 > 0, adjust it to become a
+ 3/2 inverse. */
if (u0 > 0)
{
mp_limb_t th, tl;
More information about the gmp-commit
mailing list