that's because of a misunderstanding of the C++ specification
Integral promotion
prvalues of small integral types (such as char) may be converted to prvalues of larger integral types (such as int). In particular, arithmetic operators do not accept types smaller than int as arguments, and integral promotions are automatically applied after lvalue-to-rvalue conversion, if applicable. This conversion always preserves the value.
The following implicit conversions are classified as integral promotions:
signed char or signed short can be converted to int;
unsigned char , char8_t (since C++20) or unsigned short can be converted to int if it can hold its entire value range, and unsigned int otherwise;
What happens in your specific case is not due to casting, or at least not the way you think about it.
It's the way the result of the operation (unless you specifically cast the result before calling the print method) goes though Integral promotion and thus the the compiler picks the print() method with the integer signature.
Try this code where I've defined a few whatsMyType() functions signatures.
uint8_t a = 201;
uint8_t b = 200;
void whatsMyType(uint8_t v)
{
Serial.print(v);
Serial.println(F(" is of type uint8_t"));
}
void whatsMyType(int8_t v)
{
Serial.print(v);
Serial.println(F(" is of type int8_t"));
}
void whatsMyType(int v)
{
Serial.print(v);
Serial.println(F(" is of type int"));
}
void setup() {
Serial.begin(115200);
whatsMyType( a - b ); //corect +1
whatsMyType( b - a ); //incorect -1
whatsMyType( (uint8_t) b - a ); //incorrect -1
whatsMyType( (b - a) & 255 ); //correct 255
uint8_t c = b - a;
whatsMyType( c ); //correct 255
}
void loop() {}
Serial Monitor (@ 115200 bauds) will show
1 is of type int
-1 is of type int
-1 is of type int
255 is of type int
255 is of type uint8_t
Now pick a larger type for a and b to not fall in the case of
In particular, arithmetic operators do not accept types smaller than int as arguments
and run this code:
uint16_t a = 201;
uint16_t b = 200;
void whatsMyType(uint8_t v)
{
Serial.print(v);
Serial.println(F(" is of type uint8_t"));
}
void whatsMyType(uint16_t v)
{
Serial.print(v);
Serial.println(F(" is of type uint16_t"));
}
void whatsMyType(int16_t v)
{
Serial.print(v);
Serial.println(F(" is of type int16_t"));
}
void setup() {
Serial.begin(115200);
whatsMyType( a - b ); //corect +1
whatsMyType( b - a ); //incorect -1
whatsMyType( (uint8_t) b - a ); //incorrect -1
whatsMyType( (b - a) & 255 ); //correct 255
uint8_t c = b - a;
whatsMyType( c ); //correct 255
}
void loop() {}
the result will be more in line with what you would have expected:
Serial Monitor (@ 115200 bauds) will show
1 is of type uint16_t
65535 is of type uint16_t
65535 is of type uint16_t
255 is of type uint16_t
255 is of type uint8_t
Bjarne Stroustrup, the designer of C++, supposedly said,
Using an unsigned instead of an int to gain one more bit to represent positive integers is almost never a good idea.