poly_int: Handle more can_div_trunc_p cases
can_div_trunc_p (a, b, &Q, &r) tries to compute a Q and r that satisfy the usual conditions for truncating division: (1) a = b * Q + r (2) |b * Q| <= |a| (3) |r| < |b| We can compute Q using the constant component (the case when all indeterminates are zero). Since |r| < |b| for the constant case, the requirements for indeterminate xi with coefficients ai (for a) and bi (for b) are: (2') |bi * Q| <= |ai| (3') |ai - bi * Q| <= |bi| (See the big comment for more details, restrictions, and reasoning). However, the function works on abstract arithmetic types, and so it has to be careful not to introduce new overflow. The code therefore only handled the extreme for (3'), that is: |ai - bi * Q| = |bi| for the case where Q is zero. Looking at it again, the overflow issue is a bit easier to handle than I'd originally thought (or so I hope). This patch therefore extends the code to handle |ai - bi * Q| = |bi| for all Q, with Q = 0 no longer being a separate case. The net effect is to allow the function to succeed for things like: (a0 + b1 (Q+1) x) / (b0 + b1 x) where Q = a0 / b0, with various sign conditions. E.g. we now handle: (7 + 8x) / (4 + 4x) with Q = 1 and r = 3 + 4x, gcc/ * poly-int.h (can_div_trunc_p): Succeed for more boundary conditions. gcc/testsuite/ * gcc.dg/plugin/poly-int-tests.h (test_can_div_trunc_p_const) (test_can_div_trunc_p_const): Add more tests.
This commit is contained in:
parent
29370f1387
commit
9524718654
2 changed files with 98 additions and 32 deletions
|
@ -2355,28 +2355,31 @@ can_div_trunc_p (const poly_int_pod<N, Ca> &a,
|
|||
}
|
||||
else
|
||||
{
|
||||
if (q == 0)
|
||||
{
|
||||
/* For Q == 0 we simply need: (3') |ai| <= |bi|. */
|
||||
if (a.coeffs[i] != ICa (0))
|
||||
{
|
||||
/* Use negative absolute to avoid overflow, i.e.
|
||||
-|ai| >= -|bi|. */
|
||||
C neg_abs_a = (a.coeffs[i] < 0 ? a.coeffs[i] : -a.coeffs[i]);
|
||||
C neg_abs_b = (b.coeffs[i] < 0 ? b.coeffs[i] : -b.coeffs[i]);
|
||||
if (neg_abs_a < neg_abs_b)
|
||||
return false;
|
||||
rem_p = true;
|
||||
}
|
||||
}
|
||||
/* The only unconditional arithmetic that we can do on ai,
|
||||
bi and Q is ai / bi and ai % bi. (ai == minimum int and
|
||||
bi == -1 would be UB in the caller.) Anything else runs
|
||||
the risk of overflow. */
|
||||
auto qi = NCa (a.coeffs[i]) / NCb (b.coeffs[i]);
|
||||
auto ri = NCa (a.coeffs[i]) % NCb (b.coeffs[i]);
|
||||
/* (2') and (3') are satisfied when ai /[trunc] bi == q.
|
||||
So is the stricter condition |ai - bi * Q| < |bi|. */
|
||||
if (qi == q)
|
||||
rem_p |= (ri != 0);
|
||||
/* The only other case is when:
|
||||
|
||||
|bi * Q| + |bi| = |ai| (for (2'))
|
||||
and |ai - bi * Q| = |bi| (for (3'))
|
||||
|
||||
The first is equivalent to |bi|(|Q| + 1) == |ai|.
|
||||
The second requires ai == bi * (Q + 1) or ai == bi * (Q - 1). */
|
||||
else if (ri != 0)
|
||||
return false;
|
||||
else if (q <= 0 && qi < q && qi + 1 == q)
|
||||
;
|
||||
else if (q >= 0 && qi > q && qi - 1 == q)
|
||||
;
|
||||
else
|
||||
{
|
||||
/* Otherwise just check for the case in which ai / bi == Q. */
|
||||
if (NCa (a.coeffs[i]) / NCb (b.coeffs[i]) != q)
|
||||
return false;
|
||||
if (NCa (a.coeffs[i]) % NCb (b.coeffs[i]) != 0)
|
||||
rem_p = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1899,14 +1899,19 @@ test_can_div_trunc_p_const ()
|
|||
ph::make (4, 8, 12),
|
||||
&const_quot));
|
||||
ASSERT_EQ (const_quot, C (2));
|
||||
ASSERT_EQ (can_div_trunc_p (ph::make (15, 25, 40),
|
||||
ASSERT_TRUE (can_div_trunc_p (ph::make (15, 25, 40),
|
||||
ph::make (4, 8, 10),
|
||||
&const_quot));
|
||||
ASSERT_EQ (const_quot, C (3));
|
||||
const_quot = 0;
|
||||
ASSERT_EQ (can_div_trunc_p (ph::make (15, 25, 41),
|
||||
ph::make (4, 8, 10),
|
||||
&const_quot), N <= 2);
|
||||
ASSERT_EQ (const_quot, C (N <= 2 ? 3 : 2));
|
||||
ASSERT_EQ (const_quot, C (N <= 2 ? 3 : 0));
|
||||
ASSERT_EQ (can_div_trunc_p (ph::make (43, 79, 80),
|
||||
ph::make (4, 8, 10),
|
||||
&const_quot), N == 1);
|
||||
ASSERT_EQ (const_quot, C (N == 1 ? 10 : N == 2 ? 3 : 2));
|
||||
ASSERT_EQ (const_quot, C (N == 1 ? 10 : N == 2 ? 3 : 0));
|
||||
ASSERT_TRUE (can_div_trunc_p (ph::make (3, 4, 5),
|
||||
ph::make (4, 5, 6),
|
||||
&const_quot));
|
||||
|
@ -1964,16 +1969,22 @@ test_can_div_trunc_p_const ()
|
|||
&const_quot, &rem));
|
||||
ASSERT_EQ (const_quot, C (2));
|
||||
ASSERT_KNOWN_EQ (rem, ph::make (1, 7, 6));
|
||||
ASSERT_EQ (can_div_trunc_p (ph::make (15, 25, 40),
|
||||
ASSERT_TRUE (can_div_trunc_p (ph::make (15, 25, 40),
|
||||
ph::make (4, 8, 10),
|
||||
&const_quot, &rem));
|
||||
ASSERT_EQ (const_quot, C (3));
|
||||
ASSERT_KNOWN_EQ (rem, ph::make (3, 1, 10));
|
||||
const_quot = 0, rem = 0;
|
||||
ASSERT_EQ (can_div_trunc_p (ph::make (15, 25, 41),
|
||||
ph::make (4, 8, 10),
|
||||
&const_quot, &rem), N <= 2);
|
||||
ASSERT_EQ (const_quot, C (N <= 2 ? 3 : 2));
|
||||
ASSERT_EQ (const_quot, C (N <= 2 ? 3 : 0));
|
||||
if (N <= 2)
|
||||
ASSERT_KNOWN_EQ (rem, ph::make (3, 1, 0));
|
||||
ASSERT_EQ (can_div_trunc_p (ph::make (43, 79, 80),
|
||||
ph::make (4, 8, 10),
|
||||
&const_quot, &rem), N == 1);
|
||||
ASSERT_EQ (const_quot, C (N == 1 ? 10 : N == 2 ? 3 : 2));
|
||||
ASSERT_EQ (const_quot, C (N == 1 ? 10 : N == 2 ? 3 : 0));
|
||||
if (N == 1)
|
||||
ASSERT_KNOWN_EQ (rem, ch::make (3));
|
||||
ASSERT_TRUE (can_div_trunc_p (ph::make (3, 4, 5),
|
||||
|
@ -2024,6 +2035,19 @@ test_can_div_trunc_p_const ()
|
|||
&const_quot, &rem));
|
||||
ASSERT_EQ (const_quot, C (0));
|
||||
ASSERT_KNOWN_EQ (rem, ch::make (0));
|
||||
ASSERT_TRUE (can_div_trunc_p (ph::make (9, 10, 20),
|
||||
ph::make (5, 5, 20),
|
||||
&const_quot, &rem));
|
||||
ASSERT_EQ (const_quot, C (1));
|
||||
ASSERT_KNOWN_EQ (rem, ph::make (4, 5, 0));
|
||||
ASSERT_EQ (can_div_trunc_p (ph::make (9, 11, 20),
|
||||
ph::make (5, 5, 20),
|
||||
&const_quot, &rem), N == 1);
|
||||
if (N == 1)
|
||||
{
|
||||
ASSERT_EQ (const_quot, C (1));
|
||||
ASSERT_KNOWN_EQ (rem, C (4));
|
||||
}
|
||||
}
|
||||
|
||||
/* Test the form of can_div_trunc_p that returns a polynomail quotient,
|
||||
|
@ -2093,14 +2117,14 @@ test_can_div_away_from_zero_p ()
|
|||
ph::make (4, 8, 12),
|
||||
&const_quot));
|
||||
ASSERT_EQ (const_quot, C (3));
|
||||
ASSERT_EQ (can_div_away_from_zero_p (ph::make (15, 25, 40),
|
||||
ph::make (4, 8, 10),
|
||||
&const_quot), N <= 2);
|
||||
ASSERT_EQ (const_quot, C (N <= 2 ? 4 : 3));
|
||||
ASSERT_TRUE (can_div_away_from_zero_p (ph::make (15, 25, 40),
|
||||
ph::make (4, 8, 10),
|
||||
&const_quot));
|
||||
ASSERT_EQ (const_quot, C (4));
|
||||
ASSERT_EQ (can_div_away_from_zero_p (ph::make (43, 79, 80),
|
||||
ph::make (4, 8, 10),
|
||||
&const_quot), N == 1);
|
||||
ASSERT_EQ (const_quot, C (N == 1 ? 11 : N == 2 ? 4 : 3));
|
||||
ASSERT_EQ (const_quot, C (N == 1 ? 11 : 4));
|
||||
ASSERT_TRUE (can_div_away_from_zero_p (ph::make (3, 4, 5),
|
||||
ph::make (4, 5, 6),
|
||||
&const_quot));
|
||||
|
@ -3232,6 +3256,45 @@ test_signed_can_div_trunc_p_const ()
|
|||
&const_quot, &rem));
|
||||
ASSERT_EQ (const_quot, -2);
|
||||
ASSERT_KNOWN_EQ (rem, ph::make (2, 1, 3));
|
||||
ASSERT_TRUE (can_div_trunc_p (ph::make (-9, -10, -20),
|
||||
ph::make (-5, -5, -20),
|
||||
&const_quot, &rem));
|
||||
ASSERT_EQ (const_quot, C (1));
|
||||
ASSERT_KNOWN_EQ (rem, ph::make (-4, -5, 0));
|
||||
ASSERT_EQ (can_div_trunc_p (ph::make (-9, -11, -20),
|
||||
ph::make (-5, -5, -20),
|
||||
&const_quot, &rem), N == 1);
|
||||
if (N == 1)
|
||||
{
|
||||
ASSERT_EQ (const_quot, C (1));
|
||||
ASSERT_KNOWN_EQ (rem, C (-4));
|
||||
}
|
||||
ASSERT_TRUE (can_div_trunc_p (ph::make (9, 10, 20),
|
||||
ph::make (-5, -5, -20),
|
||||
&const_quot, &rem));
|
||||
ASSERT_EQ (const_quot, C (-1));
|
||||
ASSERT_KNOWN_EQ (rem, ph::make (4, 5, 0));
|
||||
ASSERT_EQ (can_div_trunc_p (ph::make (9, 11, 20),
|
||||
ph::make (-5, -5, -20),
|
||||
&const_quot, &rem), N == 1);
|
||||
if (N == 1)
|
||||
{
|
||||
ASSERT_EQ (const_quot, C (-1));
|
||||
ASSERT_KNOWN_EQ (rem, C (4));
|
||||
}
|
||||
ASSERT_TRUE (can_div_trunc_p (ph::make (-9, -10, -20),
|
||||
ph::make (5, 5, 20),
|
||||
&const_quot, &rem));
|
||||
ASSERT_EQ (const_quot, C (-1));
|
||||
ASSERT_KNOWN_EQ (rem, ph::make (-4, -5, 0));
|
||||
ASSERT_EQ (can_div_trunc_p (ph::make (-9, -11, -20),
|
||||
ph::make (5, 5, 20),
|
||||
&const_quot, &rem), N == 1);
|
||||
if (N == 1)
|
||||
{
|
||||
ASSERT_EQ (const_quot, C (-1));
|
||||
ASSERT_KNOWN_EQ (rem, C (-4));
|
||||
}
|
||||
}
|
||||
|
||||
/* Test the form of can_div_trunc_p that returns a poly_int, for signed C. */
|
||||
|
|
Loading…
Add table
Reference in a new issue