简体   繁体   中英

What, actually, is the modulo of a negative number?

I have seen many answers for questions concerning modulo of negative numbers. Every answer placed the standard

(a/b)*b + a%b is equal to a

explanation. I can calculate any modulo with this method, and I understand it's necessary employ a modulo function that adds b to the value of a%b if it is negative for the modulo to make sense.

I am trying to make sense of this in laymen's terms. Just what is the modulo of a negative number? I read somewhere that you can calculate the proper modulo of negative numbers by hand some laymen's method of just adding numbers together. This would be helpful, because the a/b *b + a%b method is a little tedious.

To clarify, the modulo of a positive number can be interpreted in laymen's terms as the remainder when you divide the numbers. Obviously this isn't true in the case of negative numbers, so how do you properly "make sense" of the result?

This used to be implementation-defined in older revisions of C++, but now it's all fully specified:

  1. Division truncates, ie a / b is the mathematical value with the fractional part discarded. For example, 9 / -5 is the truncation of −1.8, so it's -1 .

  2. The remainder operation a % b is defined by the identity you presented. So let's compute: (a / b) * b is -1 * -5 is 5 , so 9 % -5 is 4 .

By contrast, -9 % 5 is -4 . So even though a / -b is the same as -a / b , a % -b is in general different from -a % b . (Similarly, the mathematical notion of modular equivalence, where two integers are congruent modulo n if they differ by an integral multiple of n , is invariant under replacing n with -n .)

TL;DR: There is a difference between modulo operator which is used in math and C++ % operator.

For example, let f(x) = x % 4 . Then:

x            : -9 -8 -7 -6 -5 -4 -3 -2 -1  0  1  2  3  4  5  6  7  8  9
f(x) in math :  3  0  1  2  3  0  1  2  3  0  1  2  3  0  1  2  3  0  1
f(x) in C    : -1 -0 -3 -2 -1  0 -3 -2 -1  0  1  2  3  0  1  2  3  0  1
                ^~~~~~~~~~~~~~~~~~~~~~~~^
                 This part is different

You don't need any special tricks to compute C++-style % of a negative number.

Just use a%b == a - (a/b)*b , which is derived from (a/b)*b + a%b == a .


Long answer:

Cppreference says following:

The binary operator % yields the remainder of the integer division of the first operand by the second (after usual arithmetic conversions; note that the operand types must be integral types). If the quotient a/b is representable in the result type, (a/b)*b + a%b == a . If the second operand is zero, the behavior is undefined. If the quotient a/b is not representable in the result type, the behavior of both a/b and a%b is undefined (that means INT_MIN%-1 is undefined on 2's complement systems)

Note: 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.

The important parts are:

  • (a/b)*b + a%b == a .
  • "Until C++11, if one or both operands to binary operator % were negative, the sign of the remainder was implementation-defined."

This implies that since C++11 the operator is well-defined for negative operands too.

There is no mention of any special handling of negative operands, thus we can say that above identity words for them too.

From (a/b)*b + a%b == a we can easily derive a formula for a%b :

a%b == a - (a/b)*b

If you think about it, this formula ignores the sign of b , works as if the modulo was computed with the absolute value of a , with the sign of a appended to the result.


If you want to compute the "classical" modulo, you may use something like following function:

template <typename T, typename TT> constexpr T true_mod(T a, TT b)
{
    static_assert(std::is_integral<T>::value &&
                  std::is_integral<TT>::value, "Argument types must be integral.");
    if (a >= 0)
        return a % b;
    else
        return (b >= 0 ? b : -b) - 1 + (a + 1) % b;
}

(a/b)*b + a%b is equal to a

Even if this statement is true, the result can change from a language to an other.
This difference also depends on the result of the division.

for example, in python, I have:

>>> # in python, use "//" for the floor division

>>> 3 // 4              # 0.75 is rounded to 0 : OK
0
>>> 3 % 4               # still as expected
3
>>> 0 * 4 + 3           # standard valided
3

>>> (-3) // 4           # -0.75 is rounded to -1, not 0 (the floor)
-1
>>> (-3) % 4            # the result is consistant; a modulo garanteed to be between 0 and b is very usefull
1
>>> (-1) * 4 + 1         # standard valided
-3

>>> 3 // (-4)           # -0.75 is rounded to -1, not 0 (the floor)
-1
>>> 3 % (-4)            # still a number between 0 and b
-1
>>> (-1) * (-4) + (-1)  # standard valided
3

SUMMARY:

MODULO TEST: language=python
a=3  b=4    a/b=0  a%b=3    standard:true
a=-3 b=4    a/b=-1 a%b=1    standard:true
a=3  b=-4   a/b=-1 a%b=-1   standard:true
a=-3 b=-4   a/b=0  a%b=-3   standard:true

If my memory is good, the modulo doesn't work like that in C, even if the standard is valided. It can be very disturbing.

I've juste written a little programm to test the results in C:

#include <stdio.h>

void test(int a, int b) {
    int q = a/b;
    int r = a%b;
    int ok = q*b+r == a;
    printf("a=%-2d b=%-2d   a/b=%-2d a%%b=%-2d   standard:%s\n", a, b, q, r, ok?"true":"false");
}

int main(int argc, char const *argv[]) {
    printf("MODULO TEST: language=c\n");
    test( 3, 4);
    test(-3, 4);
    test( 3,-4);
    test(-3,-4);
    return 0;
}

which gives:

MODULO TEST: language=c
a=3  b=4    a/b=0  a%b=3    standard:true
a=-3 b=4    a/b=0  a%b=-3   standard:true
a=3  b=-4   a/b=0  a%b=3    standard:true
a=-3 b=-4   a/b=0  a%b=-3   standard:true

So yes, the standard is not enough to fix a unique method for the modulo of two (negative) numbers.

You could use this code when the left number has an unknown sign :

int mod = a % b;
if (mod*b < 0) mod += b;

This code will give you a number between 0 and b all the time, like in python ( 0 <= mod < b , or b < mod <= 0 if b is negative).
The * b is useless if b is strictly positive (in most of the cases).


EDIT

Using a XOR is better than a multiplication, as it prevents overflow.

int mod = a % b;
if ((mod < 0) ^ (b < 0)) mod += b;

And when b is strictly positive:

int mod = a % b;
if (mod < 0) mod += b;

EDIT 2 (2018-10-09)

Better use this, to use python-style division (modulo between 0 included and b excluded) in C:

int q = a / b;
int r = a % b;
if ((b<0) ? (r<0) : (r>0)) {
    q -= 1;
    r += b;
}

It prevents "extremes" cases like b is negative and divides a (like 6 % (-3) ). The result must be 0 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM