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 anunsigned charvalue, which has rank lower thanint, it will first undergo integer promotion, meaning it will be converted to anintwith the same value (assumingintcan hold all values ofunsigned char, which is practically always the case).The literals
1and8are also of typeint, so that then the operations will be performed in the common typeint, also with anintresult. Notably, this means all arithmetic in your example happens with signed values.Only when you assign back to the
unsigned charvariable is the signedintvalue converted back to an unsignedunsigned charvalue.So both
result1 - 1andresult2 - 1areints with value-1.The way
%treats negative values in C++,(result1 - 1) % 8is then also anintwith value-1.Conversion of
-1tounsigned charresults in255if the width of(unsigned) charis8because such conversion is specified to keep the value congruent 2^n where n is the width of the target type.However, conversion of
result2 - 1tounsigned charto store inresults2also results in255, so inresult2 % 8the operation is255 % 8(inint) which gives in theintresult7, which converted back tounsigned charis still7.Technically it is also allowed for
unsigned charto have the same width asint(although in that case both need to have at least16bit width, because that is the minimum width required forintby the standard.)In that case,
intcan't hold all values ofunsigned charand integer promotion would promote tounsigned intinstead. Then, the usual arithmetic conversions will preferunsigned intover theinttype of the literal operands and so all arithmetic will be done inunsigned 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.