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.
Yes, the result is deterministic, not (compiler's) implementation defined. Here follows the motivation for C++11 (it should be possible to do the same for other), following the document here (link suggested here)
It's necessary to combine all of the following:
To find the usual arithmetic conversion we need to go to the incipit of Chapter 5, paragraph 9:
So, integral promotion, citing from 4.5:
So:
We have a relational operator, the usual arithmetic conversions will be used. These oblige to apply integral promotion. An integral promotion for
uint8_tandint8_tis possible toint, so it is obligatorily applied.Therefore the comparison between a
uint8_tandint8_tis transformed by the compiler into a comparison between 2int. There is no undeterministic behaviour.There was a similar Q/A here (about
shorttype though), which led me to the right path.Note the following contradiction though: Relational operators return a boolean value (5.9.1), yet they use the usual arithmetic conversions which is used to obtain 2 operands of the same type. But, here lays the problem, the definition of usual arithmetic conversion says that the common type will be also the type of the result, which isn't the case for relational operators!!
The contradiction is not there for C11, where the result returned by relational operators is indeed an
int. (Thanks chux)