Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't a negative number modulo a vector size give a negative number? [duplicate]

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main()
{
  vector<int> v = {1, 2, 3, 4, 5, 6, 7};
  int i = -4;

  cout << i << endl;
  cout << v.size() << endl;
  cout << i % v.size() << endl;
  cout << -4 % 7 << endl;
}

The above code prints:

-4
7
5
-4

Can someone please explain why i % v.size() prints 5 instead of -4? I'm guessing it has something to do with vector.size(), but unsure what the underlying reasoning is. Thanks in advance.

like image 416
George Zhang Avatar asked Jun 15 '20 06:06

George Zhang


People also ask

What happens if you modulo a negative number?

If both the divisor and dividend are negative, then both truncated division and floored division return the negative remainder.

Can modulo give negative?

Can a modulus be negative? % can be negative as it is the remainder operator, the remainder after division, not after Euclidean_division. Since C99 the result may be 0, negative or positive. The modulo OP wanted is a classic Euclidean modulo, not % .

How do you avoid negative modulo?

Therefore, in C/C++ language we always find remainder as (a%b + b)%b (add quotient to remainder and again take remainder) to avoid negative remainder.

Does modulo work on negative numbers in C?

Modulus operator with negative numbers If we have negative numbers, the result will be based on the left operand's sign, if the left operand is positive – the result will be positive, and if the left operand is negative – the result will be negative.


4 Answers

The operands of % undergo the usual arithmetic conversions to bring them to a common type, before the division is performed. If the operands were int and size_t, then the int is converted to size_t.

If size_t is 32-bit then -4 would become 4294967292 and then the result of the expression is 4294957292 % 7 which is actually 0.

If size_t is 64-bit then -4 would become 18,446,744,073,709,551,612 and the result of this % 7 is 5 which you saw.

So actually we can tell from this output that your system has 64-bit size_t.

like image 78
M.M Avatar answered Oct 22 '22 06:10

M.M


In C++ the modulus operator is defined so that the following is true for all integers except for b == 0:

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

So it is forced to be consistent with the integer division, which from C++ 11 onwards truncates to zero even for negative numbers. Hence everything is well defined even for negative numbers.

However, in your case you have an signed / unsigned division (because .size() returns unsigned) and the usual signed/unsigned rules apply. This means that in this case all arguments are converted to unsigned before the operation is carried out (see also Ruslan's comment).

So -4 is converted to unsigned (and becomes a very large number) and then modulo is carried out.

You can also see this as 5 is not a correct answer for -4 modulo 7 with any definition of integer division (3 would be correct).

Arithmetic rules with C and C++ are not intuitive.

like image 43
Andreas H. Avatar answered Oct 22 '22 07:10

Andreas H.


Because v.size return size_t.

cout << -4 % size_t(7) << endl; // 5

Take a look modulo-operator-with-negative-values

UPD: and signed-int-modulo-unsigned-int-produces-nonsense-results

like image 40
Anton Shwarts Avatar answered Oct 22 '22 05:10

Anton Shwarts


This is due to the type of v.size(), which is an unsigned type. Due to integer promotion, this means that the result will also be treated as unsigned, despite i being a signed type.

I am assuming you are compiling on 64 bit. This means that in addition to promotion to unsigned, the result will also be of the 64 bit type unsigned long long. Step by step:

  1. unsigned long long _i = (unsigned long long)-4; // 0xFFFFFFFFFFFFFFFC!
  2. unsigned long long result = _i % (unsigned long long)7; // 5

Since presumably you want to preserve the signedness of i, in this case it is enough to cast v.size() to a signed type to prevent the promotion to unsigned: i % (int)v.size() will give -4.

like image 33
Matti Avatar answered Oct 22 '22 07:10

Matti