https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125767
Bug ID: 125767
Summary: can_div_away_from_zero_p incorrectly depends on
can_div_trunc_p succeeding
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: middle-end
Assignee: unassigned at gcc dot gnu.org
Reporter: Chris.Bazley at arm dot com
Target Milestone: ---
The can_div_away_from_zero_p function seems to have a bug which causes it to
return false for some inputs that should cause it to return true. Its
documented contract is:
/* Return true if there is some constant Q and polynomial r such that:
(1) a = b * Q + r
(2) |a| <= |b * Q|
(3) |r| < |b|
Store the value Q in *QUOTIENT if so. */
Let's plug in some values from a failing case, where N=2:
a = {16,0}
b = {16,16}
i.e. round_away_from_zero((16 + 0i) / (16 + 16i))
16 + 16i >= 16 + 0i, therefore expect Q = 1 (as for 16/16 or 16/32)
(1) a = b * Q + r
a - b * Q = r
r = a - b * 1 = {16,0} - {16,16} = {0,-16}
(2) |a| <= |b * Q|
{16,0} <= {16,16} * 1 is true
(3) |r| < |b|
|{0,-16}| < |{16,16}| is true
The cause seems to be over-reliance on another function, can_div_trunc_p. If
can_div_trunc_p fails then can_div_away_from_zero_p fails too:
if (!can_div_trunc_p (a, b, quotient))
return false;
Because polynomial division truncated toward zero can fail to produce a
constant quotient in cases where polynomial division rounded away from zero can
produce a constant quotient, it is not sufficient for the implementation of
can_div_away_from_zero_p to simply adjust the quotient produced by
can_div_trunc_p.
The implementation of can_div_trunc_p is correct as far as I know. Its
documented contract is:
/* Return true if there is some constant Q and polynomial r such that:
(1) a = b * Q + r
(2) |b * Q| <= |a|
(3) |r| < |b|
Store the value Q in *QUOTIENT if so. */
Let's plug in the same values as above:
a = {16,0}
b = {16,16}
i.e. trunc((16 + 0i) / (16 + 16i))
16 + 16i >= 16 + 0i therefore expect Q = 1 (as for 16/16 or 16/32)
(1) a = b * Q + r
a - b * Q = r
r = a - b * 1 = {16,0} - {16,16} = {0,-16}
(2) |b * Q| <= |a|
|{16,16} * Q| <= |{16,0}|
|{16,16}| <= |{16,0}| is indeterminate because it depends on the value
of 'i':
If i == 0 then (16 + 0i) / (16 + 16i) = 16 / 16 = 1.
If i > 0 then (16 + 0i) / (16 + 16i) = 16 / (16 (1 + i)) = 1 / (1 + i)
which is not "some constant Q".
(3) |{0,-16}| < |{16,16}| is true.
Because (2) could be false, Q = 1 does not satisfy the constraints of
can_div_trunc_p.