Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

signed int modulo unsigned int produces nonsense results

I need to perform a real mathematical modulo in C. It makes sense for me to allow negative numbers for the moduled argument, since my modular calculations can produce negative intermediate results, which must be put back into the least residue system. But it makes no sense to allow negative module, therefore i wrote

unsigned int mod( int x, unsigned int m )
{
    int r = x % m;
    return r >= 0 ? r : r + m;
}

However calling such function with negative number and positive module

printf("%u\n", mod(-3, 11));

produces output

1

And i don't understand why. Could you please explain?

EDIT: I know operator % is different from mathematical modulo and i know how it is defined for positive and negative numbers. I was asking what it will do for different signedness, not different sign.

like image 964
Youda008 Avatar asked Dec 23 '22 19:12

Youda008


2 Answers

clang with -Wconversion enabled clearly pinpoints your mistake:

prog.cc:3:15: warning: implicit conversion changes signedness: 'unsigned int' to 'int' [-Wsign-conversion]
    int r = x % m;
        ~   ~~^~~
prog.cc:3:13: warning: implicit conversion changes signedness: 'int' to 'unsigned int' [-Wsign-conversion]
    int r = x % m;
            ^ ~
prog.cc:4:21: warning: operand of ? changes signedness: 'int' to 'unsigned int' [-Wsign-conversion]
    return r >= 0 ? r : r + m;
    ~~~~~~          ^
prog.cc:4:25: warning: implicit conversion changes signedness: 'int' to 'unsigned int' [-Wsign-conversion]
    return r >= 0 ? r : r + m;
                        ^ ~
prog.cc:9:12: warning: implicit conversion changes signedness: 'unsigned int' to 'int' [-Wsign-conversion]
    return mod(-3, 11);
    ~~~~~~ ^~~~~~~~~~~

live example on wandbox


When converted to unsigned int, -3 becomes 4294967293.

4294967293 % 11 is equal to 1.

like image 76
Vittorio Romeo Avatar answered Jan 06 '23 08:01

Vittorio Romeo


See C11 6.5.5 (Multiplicative operators) /3:

The usual arithmetic conversions are performed on the operands.

The usual arithmetic conversions is defined by 6.3.1.8. The relevant part is:

Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.

So in x % m, the x is first converted to unsigned int.

To avoid this behaviour you could use x % (int)m,although this will malfunction if m > INT_MAX. If you want to support m > INT_MAX and also negative x, you'll have to use slightly more complicated logic.

like image 31
M.M Avatar answered Jan 06 '23 07:01

M.M