Why doesn't the comparison between signed char and unsigned int work correctly?
We have:
unsigned x = 1;
signed char y = -1;
this expression:
x > y
used as the control expression of an if statement.
After normal arithmetic conversions, the right-hand operand y
will be converted to a value unsigned int
. Converting a negative value signed char
-1
will result in a huge value unsigned int
(equal UINT_MAX
).
So the expression x > y
will evaluate as:
1U > UINT_MAX
which is always false (i.e. evaluates to 0).
This is the short version. To explain how we arrived at this result using standard C rules, I'll explain it below.
Here's how it's done:
The relational operator is used >
, here he is talking about relational operators:
Relational Operators (C99, 6.5.8p3) "If both operands are of arithmetic type, normal arithmetic conversions are performed."
Okay, in our example, both operands are integer types, and integer types are arithmetic. Thus, the usual arithmetic conversion will be performed. What are the usual arithmetic conversions?
Plain arithmetic conversions (C99, 6.3.1.8p1) "Otherwise, integer promotions are performed on both operands. The following rules are then applied to promoted operands:`
Okay, the first whole promotions are done on each operand. How are entire promotions executed?
Integer promotions (C99, 6.3.1.1p2) "if an int can represent all values ββof the original type, the value is converted to int, otherwise it is converted to unsigned int. These are called integer promotions.
y
is of type signed char
, so it is first promoted to int
after whole promotions, and x
is of type unsigned int
and remains unsigned int
.
Then the usual arithmetic conversions will find a common type between both operands. In our case, this means the following:
Conventional Arithmetic Conversions (Set) (C99, 6.3.1.8p1) "Otherwise, both operands are converted to an unsigned integer corresponding to the type of the signed integer operand."
unsigned int
has the same conversion rank as the type int
(remember, signed char
was promoted to int
), so promoted y
will be converted from int
(after being promoted) to unsigned int
. For information, conversion integers are defined in (C99, 6.3.1.1p1). As you can see, it unsigned int
wins for int
normal arithmetic conversions, another way to say it is to say which unsigned
is sticky.
Now, how is the value int
-1
( signed char
-1
after it promoted to int
) converted to a value unsigned int
? `. Here's what C says about integer conversion in this particular case:
Integer conversions (C99, 6.3.1.3p2) "Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting more than the maximum value that can be represented to the new type until the value is in the range of the new type. "
This paragraph is written in such a way that its meaning does not depend on the signed number. For the two complementary views, this means the value is int
-1
converted to (UINT_MAX + 1) - 1
, which is equal to UINT_MAX
. So, in our particular case
x > y
equivalent to
1U > UINT_MAX
equivalent to
0
source to share
If you are comparing variables that are not of the same type, one of them must be coerced into the type of the other before the comparison is made.
"Shorter" types will be promoted to "longer" types.
In this case, signed char y
will be converted to unsigned int
with value UINT_MAX
.
In particular, assuming the compiler is using 32-bit ints
:
signed char -1 = 0xff becomes
-> signed int -1 = 0xffffffff becomes
-> unsigned int 0xffffffff = UINT_MAX
Consequently, x < y
.
source to share