The following program:
#include <iostream>
#include <string>
int main ()
{
unsigned char result1 {0};
unsigned char result2 {0};
result1 = (result1 - 1) % 8;
result2 = result2 - 1;
result2 = result2 % 8;
std::cout << "result1 is " << std::to_string (result1) << '\n';
std::cout << "result2 is " << std::to_string (result2) << '\n';
}
results in the following output:
result1 is 255
result2 is 7
Why is it that the calculation of result1 and result2 has different results?
I tried several compilers but they all produce the same result so it must be something I don't understand.
Arithmetic in C++ is never done in a type with rank lower than int
.
If you apply -
or %
to an unsigned char
value, which has rank lower than int
, it will first undergo integer promotion, meaning it will be converted to an int
with the same value (assuming int
can hold all values of unsigned char
, which is practically always the case).
The literals 1
and 8
are also of type int
, so that then the operations will be performed in the common type int
, also with an int
result. Notably, this means all arithmetic in your example happens with signed values.
Only when you assign back to the unsigned char
variable is the signed int
value converted back to an unsigned unsigned char
value.
So both result1 - 1
and result2 - 1
are int
s with value -1
.
The way %
treats negative values in C++, (result1 - 1) % 8
is then also an int
with value -1
.
Conversion of -1
to unsigned char
results in 255
if the width of (unsigned) char
is 8
because such conversion is specified to keep the value congruent 2^n where n is the width of the target type.
However, conversion of result2 - 1
to unsigned char
to store in results2
also results in 255
, so in result2 % 8
the operation is 255 % 8
(in int
) which gives in the int
result 7
, which converted back to unsigned char
is still 7
.
Technically it is also allowed for unsigned char
to have the same width as int
(although in that case both need to have at least 16
bit width, because that is the minimum width required for int
by the standard.)
In that case, int
can't hold all values of unsigned char
and integer promotion would promote to unsigned int
instead. Then, the usual arithmetic conversions will prefer unsigned int
over the int
type of the literal operands and so all arithmetic will be done in unsigned int
.
Under these circumstances both of your outputs will give 7
. Whether any C++ implementation with these properties actually exists, I don't know, but it is permitted by the standard.