I run this simple program:
#include <stdint.h>
#include <iostream>
int main() {
uint8_t x = 100;
int8_t y = -128;
if (x < y) {
std::cout << (int) x << " is less than " << (int) y << std::endl;
} else {
std::cout << (int) y << " is less than " << (int) x << std::endl;
}
}
With output, correctly:
-128 is less than 100
I was at first surprised to see no signedness warning was generated.
I was then surprised not to have a wrong conversion going on (-128 -> 255) and therefore not getting a wrong result.
Then I read this:
1 A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (4.13) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int. [§ 4.5]
Link to standard n2356
What does it mean "Can be converted"? Is it up to the compiler implementation if this conversion will happen and therefore if this expression will return a correct value?
The point is that the compiler shall search for a common type to which convert the 2 operands, but I don't find any obligation in the standard to do its best so that this common type is able to represent all possible values of both the 2 input types.
Note: I tagged also C as this case seems to be also applicable to it.
Related question: Comparison signed and unsigned char. Also this.