Re: Warnings when using % operator

From: Derrick Coetzee (dcnews_at_moonflare.com)
Date: 09/16/04


Date: Thu, 16 Sep 2004 03:20:36 -0400

Tim Rentsch wrote:
> This brings up an interesting challenge - how can IMOD be written
> so that (1) it handles any combination of signed and unsigned
> operands, and (2) doesn't generate compiler warnings like the
> one mentioned in the earlier posting caused by comparing an
> unsigned value to be less than zero? The desired semantics
> are to return the smallest non-negative 'r' such that
>
> i == m * j + r
>
> with 'm' and 'r' being integers. For simplicity feel free to
> assume that j != 0 and the value of 'r' can be represented as
> a value of the type of 'i % j'.

Neither assumption is needed: 0 <= r < |j|, and % takes the type of j.
The answer seems simple to me - transform before, not afterwards:

#define IMOD(i,j) ((i) <= 0 \
                   ? ((j) <= 0 ? -j - ((-i) % (-j)) : j - ((-i) % (j))) \
                   : ((j) <= 0 ? (i) % (-j) : (i) % (j)))

Then, if i == m*j + r, then all the following also hold:
  i == (-m)*(-j) + r // i pos, j neg
-i == (m-1)*(-j) - j - r // i neg, j neg, -j > r
-i == (m-1)*j + j - r // i neg, j pos, j > r
The remainder parts are all the smallest positive possible, since
they're all positive and all < j.

I'm avoiding the compiler warning for unsigned i and j by using "i <= 0"
and "j <= 0" rather than "i < 0" and "j < 0" (it doesn't matter which
branch we use for zero). Here's a complete sample that runs and produces
correct answers with no warnings with gcc -Wall:

#include <stdio.h>

#define IMOD(i,j) ((i) <= 0 \
                   ? ((j) <= 0 ? -j - ((-i) % (-j)) : j - ((-i) % (j))) \
                   : ((j) <= 0 ? (i) % (-j) : (i) % (j)))

int main() {
     printf("%d %d %d %d %d\n", IMOD(5u,3u), IMOD(5,3), IMOD(-5,3),
                                IMOD(5,-3), IMOD(-5,-3));
     return 0;
}

> By the way, am I right in thinking that in a standard-compliant
> compilation the expression '-1 % 2 > 0' will always be zero (that
> is, false)?

Nope. "If both operands are nonnegative then the remainder is
nonnegative; if not, the sign of the remainder is
implementation-defined." - ISO C++ Standard, 5.6

However, there is an implicit constraint in this promise:
"(a/b)*b + a%b is equal to a"
But we know how multiplication and integer division behave; if you work
it out, (a/b)*b must have the sign of a, and be no further from zero
than a. Consequently, if a is negative, a%b must be nonpositive, in
order to "get back" to a. Similarly, if a is positive, a%b must be
nonnegative, regardless of the sign of b, to satisfy this constraint. I
think pretty much all implementations overlooked this, though.

-- 
Derrick Coetzee
I grant this newsgroup posting into the public domain. I disclaim all
express or implied warranty and all liability. I am not a professional.


Relevant Pages

  • Re: Problem(s) with round-to-nearest
    ... the usual integer division, etc. correspond to rounding towards zero, ... remainder, I have reduced the problem analytically to an equation ... For the Nth-Root function: d is never zero... ...
    (sci.math)
  • Problem(s) with round-to-nearest
    ... the usual integer division, etc. correspond to rounding towards zero, ... remainder, I have reduced the problem analytically to an equation ... For the Nth-Root function: d is never zero... ...
    (sci.math)
  • Re: How can i find out the quotient?
    ... showing how to calculate both the quotient and remainder. ... But now I see that the quotient appears at the very top ... ... When it's divisor times x^n, write a '1' someplace; when it's zero, write '0' going ... with an encoded message which is a multiple of the divisor polynomial. ...
    (sci.math)
  • Re: What logical results could be reasoned from definition?
    ... Remainder of number divided by two is Zero defined Even number ... only is the logic thinking by researcher self before this century. ... A is bigger than B and C belong to D defined E ...
    (sci.logic)