### 16 September 2007 3:33 pm (c)

This may be something basic, but I lost a bit of time last week trying to find a bug at work, so I thought it was worth mentioning it.

When comparing signed and unsigned expressions of the same size, the compiler produces what it might be unexpected results. Suppose you have this code:

#include <stdio.h> int main (void) { unsigned short int a = -12; signed short int b = -12; printf ("%s\n", (a == b) ? "OK" : "failed"); return 0; }

Here, you would probably expect an "*OK*" output, but surprisingly you get "*failed*". Why is this? If you know about integer promotion, you may skip to the end if you don't want to follow all the process.

Fortunately, I received a copy of K&R last week, so I started digging into the issue to try to understand why this was happening.

About equality operators, K&R section A7.10 reads

The equality operators follow the same rules as the relational operators...

Section A7.9, about relational operator, reads

The usual arithmetic conversions are performed on arithmetic operands...

So, how these arithmetic conversions work?

This is also clearly explained in K&R section A6.5 (the same rules apply to the C99 standard, section 6.3.1.8, Usual arithmetic conversions). The part that interests us is after having evaluated all the real type conversions, when it says

Otherwise, the integer promotions are performed on both operands...

So, before performing the comparison of our operands, both undergo integer promotion. Integer promotion (K&R, section A6.2) says that

If an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int.

An *int* can represent all the values of our operands, so after the integer promotion is performed, both operands have *int* type. Having a look back to our operands, we know that the *a* variable holds the value *0xFFF4*, and after applying the integer promotion, it maintains the value. The same happens with variable *b*, that holds *0xFFFFFFF4* to represent *-12*. Clearly, both values are different and the check fails.

At the end of K&R section A6.5 this is explicitly explained

The new rules are slightly more complicated, but reduce somewhat the suprises that may occur when an unsigned quantity meets signed. Unexpected results may still occur whan an unsigned expression is compared to a signed expression of the same size.

Basically, what's going on here, is that both variables undergo integer promotion. *b* is signed, and it is sign-extended. This sign-extension is maintained due to the integer promotion.

Note, that this issue does not occur with 32-bit values, as both operands would be "0xFFFFFFF4".

So, be careful when comparing unsigned and signed types.

23 September 2009 4:40 pm

Can u plz tell me ....how a 16bit int [b here] start representing value corresponds to 32 bit? It seems bizarre to me as i m not able to dig it out with my present understanding. Kindly update it to me if anybody is aware of the same.

30 September 2009 10:49 am

This is what integer promotion does. As both operands are converted to 32-bits in order to perform the comparison, the 16-bit signed integer is converted to a 32-bit signed integer. Signed integer follow two's complement representation, so the sign is extended.

So, you are comparing 0x0000FFF4 with 0xFFFFFFF4, which are different.

There would be no problem if the comparison was done directly with 16-bit values.

30 September 2009 1:26 pm

There is some confusion:

Acc to my understanding in case of integer promotion signed int becomes corresponding unsigned int(sign bit start using in forming value)

Now in this case both signed-unsigned int are of same size(16-bit).

Why they have to convert in 32 bits for the comparison??

One question on the same:

"There would be no problem if the comparison was done directly with 16-bit values."

If the size of int be 32 bits while size of unsigned int be 16bits then also integer value will just corresponding the value of the possible unsigned int??

30 September 2009 2:07 pm

> Acc to my understanding in case of integer promotion signed int becomes

> corresponding unsigned int(sign bit start using in forming value)

Not always, check C99 standard section 6.3.1.8.

> Now in this case both signed-unsigned int are of same size(16-bit).

> Why they have to convert in 32 bits for the comparison??

This is what the standard says (6.3.1.1):

"

If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer

promotions.)

The integer promotions preserve value including sign.

"

This is what's happening, both operands are converted to ints.

> If the size of int be 32 bits while size of unsigned int be 16bits then also integer

> value will just corresponding the value of the possible unsigned int??

I'm not sure if I undertand this, but in this case the 16 bit value would be converted to int.

30 September 2009 5:52 pm

Sorry, but i again have a query:

>>I’m not sure if I undertand this, but in this case the 16 bit value >>would be converted to int.

Again, with my understandings integer promotion only talks about promotion for signed integer and not for unsigned one. So, the value should remain of 16-bit.

But we can confirm d same by running a testcase.

>>The integer promotions preserve value including sign.

Is that mean event after integer promotion signed int will retain its sign bit and not consider it as a part of value.

Lets see this loop:

unsigned int j = 6;

int i;

int flag = 0;

for(i = -1;i < j; i++)

{

..........printf("No integer promotion");

..........flag = 1;

..........break;

}

if(!flag)

{

printf("Integer promotion");

}

Now in this case "i" will be treated as 0xffffffff and not not consider MSB bit as a sign bit. We should have a messsage : "Integer promotion"

Let me clear :)

PS: I have just written the code so it might not be intact as such.

30 September 2009 6:10 pm

I go through this corresponding section in K&R ... n it seems ur explanation is inline with it.

But i would admire...if u give ur remarks on my last query.

Thankx a lot in advance.

1 October 2009 5:17 pm

Yes, in the example you get "Integer promotion". Here both operands are converted to unsigned int, following the standard:

"Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type."

So,

1. We need to widen short values.

2. We perform arithmetic conversions.

For the 16-bit case (in the post):

1. We follow 6.3.1.1 to widen both operands to int (preserving the sign for the signed short int).

2. Then we have two int values that do not match.

For the 32-bit case (your example):

1. We do not need to widen any operand.

2. Both operands are converted to unsigned int.

Hope this helps.