3

Hi I'm aware in C the modulo of a negative dividend results in a negative modulo, but what I found is that the modulo of a negative dividend using a long unsigned divisor or a long long unsigned divisor results in a positive modulo!

Here's an example:

#include <stdio.h>

int main(int argc, char** argv)
{
    long long int a = -2205348223670655684LL;
    printf("%lld %lld %lld %lld %lld %lld\n", a % 20, a % 20L, a % 20LL, a % 20U, a % 20LU, a % 20LLU);
    return 0;
}

and the output:

$> ./a.out
-4 -4 -4 -4 12 12

Can anyone explain why? I've tried it with GCC 4.8 and 5.1

rewritten
  • 16,280
  • 2
  • 47
  • 50
tsilvestre
  • 173
  • 1
  • 5
  • 3
    "modulo of a negative dividend results in a negative modulo" wrong. This is implementation-defined. – n. m. could be an AI Nov 19 '15 at 09:50
  • @n.m. ... in earlier C standards. – user694733 Nov 19 '15 at 09:51
  • Also I'm aware in pre C11 standard this modulos with negative the sign was implementation specific, but anyways I'm interested too in the rationale of the implementation and I can't find in the current standard anything that explains this behavior either – tsilvestre Nov 19 '15 at 09:51
  • @user694733: This was standardized? With C11 I presume? Can you give the section? (Genuine interest here.) – DevSolar Nov 19 '15 at 09:52
  • 1
    Look at this thread [link](http://www.cplusplus.com/forum/general/19502/) – Jerome Nov 19 '15 at 09:53
  • @n.m.: There is no room for implementation choices here, at least as of C11: division must round towards zero, and remainder must be compatible with division. –  Nov 19 '15 at 09:53
  • 2
    @DevSolar N1570 6.5.5.6: *"... If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a; otherwise, the behavior of both a/b and a%b is undefined."* – user694733 Nov 19 '15 at 09:54
  • @user694733 ouch, they have changed it. How unfortunate – n. m. could be an AI Nov 19 '15 at 09:58
  • @Lingxi: One, those functions are *defined* in terms of the `%` operator (for C99 and C++11 at least) so that doesn't help. Two, that's not the problem here, it's type promotion. ;-) – DevSolar Nov 19 '15 at 10:01
  • @DevSolar Since C++11, `std::div` is defined in terms of `%` for `%` is well defined since C++11. Quoted from cppref: Until C++11, if one or both operands to binary operator % were negative, the sign of the remainder was implementation-defined, as it depends on the rounding direction of integer division. The function std::div provided well-defined behavior in that case. – Lingxi Nov 19 '15 at 10:08
  • @Lingxi: Yes, well, the `div()` / `ldiv()` / `lldiv()` functions were defined in terms of `%` since C99, while the `%` operator was only well-defined in the above sense since C11. That's what one gets for tagging a question both C *and* C++. ;-) – DevSolar Nov 19 '15 at 10:22
  • [Does either ANSI C or ISO C specify what -5 % 10 should be?](http://stackoverflow.com/q/3609572/995714), [Modulo operation with negative numbers](http://stackoverflow.com/q/11720656/995714), [Why is the behavior of the modulo operator (%) different between C and Ruby for negative integers?](http://stackoverflow.com/q/24074869/995714) – phuclv May 13 '17 at 13:11

2 Answers2

4

C99 section 6.5.5/6 requires that when a/b is representable:

(a/b) * b + a%b shall equal a

And from 6.5.5/3

The usual arithmetic conversions are performed on the operands.

For more details about arithmetic conversion, please check section 6.3.1.8.

Now it seems on your implementation sizeof(long) = sizeof(long long) = 64 bits

For the first 4 cases, signed or unsigned divisor can be changed to type of numerator (i.e. long long int), but in last 2 cases dividend must be changed(casted or reinterpreted as) to unsigned type as divisor has same width and is unsigned causing the result.

On some systems where sizeof(long) < sizoef(long long), second last result should be different.

Mohit Jain
  • 30,259
  • 8
  • 73
  • 100
3

There is no such thing as a (built-in) arithmetic operation performed with one signed and one unsigned operand; both operands of a binary operation get promoted to the same type.

In your last two examples, that type is unsigned long long; so a gets converted to an unsigned value, and the remainder is computed using that.