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.

Reply via email to