8

I have searched this site for an answer and found many responses to unsigned/signed comparison but this problem is that only unsigned parameters are compared but still it works funny.

The problem with the following code is that the first if-statment does not happen ("hello") where as the second ("world") does. This I have interpreted as the calculation that is done inside the if-statment generates a negative number but the exact same calculation done with the result saved to a variables does not (even though the result is being saved to a signed variable).

The compiler used is gcc 4.4.

unsigned short u16_varHigh;  
unsigned short u16_varLow;  
unsigned short u16_Res1;  
signed short   s16_Res1;  

u16_varHigh = 0xFFFF;  
u16_varLow = 10;

u16_Res1 = u16_varLow - u16_varHigh; // response is 11 as expected  
s16_Res1 = u16_varLow - u16_varHigh; // response is 11 as expected

// Does not enter  
if( (u16_varLow - u16_varHigh) > (unsigned short)5 )  
{  
 printf( "hello" );  
}

// Does enter  
if( (unsigned short)(u16_varLow - u16_varHigh) > 5 )  
{  
 printf( "world" );  
}

Can anyone explain this for me and perhaps come up with a solution for a fix so that the first if-statement works as well?

Ferruccio
  • 98,941
  • 38
  • 226
  • 299
Wilmer
  • 83
  • 1
  • 3
  • possible duplicate of [Question about C integral promotion rules.](http://stackoverflow.com/questions/2280663/question-about-c-integral-promotion-rules) – unwind Nov 17 '10 at 12:32
  • 3
    I disagree. On similar topic, but not duplicate. – Kos Nov 17 '10 at 12:41

4 Answers4

4

In the expression:

if( (u16_varLow - u16_varHigh) > (unsigned short)5 )  

(u16_varLow - u16_varHigh) will be promoted to an int and evaluate to -65525. The fix for your problem is to cast to an unsigned type, like you do in the "Does enter"-code.

The reason s16_Res1 = u16_varLow - u16_varHigh; yields 11 is that the result of the subtraction, -65525, doesn't fit in a short.

Andreas Brinck
  • 51,293
  • 14
  • 84
  • 114
  • It is actually `u16_varLow` and `u16_varHigh` that are individually promoted to `int`, prior to the subtraction. – caf Nov 18 '10 at 04:00
  • Is it two individual casts that is done? The following if-statement will not be entered: u16_varHigh = 0xff00;
    if( u16_varHigh < -5 )... The decision to make it signed is because of the substraction. So compare a unsigned (with high bit set) will be considered unsigned, but compare the difference between 2 unsigned an it is considered signed. It feels wrong somehow...
    – Wilmer Nov 18 '10 at 15:34
  • It's also worth noting that the `(unsigned short)` has no effect (on a 32-bit machine). `(unsigned short)5` is promoted to `int` before being considered as input to the comparison, if all values of `unsigned short` fit into an `int`. If you did this on a processor in which short and int were the same width, `(unsigned short)5` would promote to `unsigned`, as would the lhs, and the condition would be true. – greggo Apr 20 '15 at 19:19
  • @Wilmer I don't quite follow you but I think you've misunderstood. The 'difference between two unsigned' in the example is actually the difference between two ints, since the u16 are both converted to int before the subtraction is considered. If you instead did `(unsigned)u16_varLow-(unsigned)u16_varHigh`, the result of that subtraction would be unsigned. – greggo Apr 20 '15 at 19:24
2

In the other answers we have seen that

u16_varLow - u16_varHigh

for you (with 16 bit short and 32 bit int) is equivalent to

(int)u16_varLow - (int)u16_varHigh

and thus its result is the int value -65525. Thus the assignment

s16_Res1 = u16_varLow - u16_varHigh; 

is equivalent to

s16_Res1 = -65525;

which in your case of 16 bit short yields "undefined behavior". You are just unlucky that your compiler decides to assign 11, instead. (Unlucky because I think that it is better to fail early.)

In contrast to that

u16_Res1 = -65525; 

is a valid assignment since u16_Res1 is of an unsigned type and arithmetic of unsigned types is modulo the appropriate power of two.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
2

In the "usual arithmetic conversions", types smaller than int are promoted to either int or unsigned int before they are used in most expressions. The rule is that if int can represent all the values of the smaller type, then it is promoted to int; otherwise it is promoted to unsigned int. This is often considered something of a wart, because in many cases it causes unsigned char and unsigned short values to be promoted to int.

This is exactly what you're seeing - u16_varLow and u16_varHigh and (unsigned short)5 are all promoted to int before the subtraction and comparison, which then happen using int. If you wish to be certain that an expression will use unsigned arithmetic, you must do it in unsigned int, not unsigned short:

if( ((unsigned)u16_varLow - (unsigned)u16_varHigh) > 5U )
caf
  • 233,326
  • 40
  • 323
  • 462
1

The first one, if( (u16_varLow - u16_varHigh) > (unsigned short)5 ) will never pass, as (u16_varLow - u16_varHigh) returns negative number, because it's treated as an integer. The second one casts the same negative number, to unsigned short, that's why it passes.

Note - you know that all this is platform-dependent, right? The size of short, int, etc. depends on the concrete platform.

Kiril Kirov
  • 37,467
  • 22
  • 115
  • 187
  • @Note: I am aware of this but I thank you anyway (Because hade I _not_ been aware of this, you would have saved me alot of work) – Wilmer Nov 18 '10 at 15:52