>>> Sorry, here is the code for the msp430:
>>>
>>> #include<io.h>
>>> int main(void)
>>> {
>>>      short x = -32768;
>>>      unsigned long  y = -x;
>>>      LPM0;
>>> }
>>>
>>> And same code as my previous posting for the PC:
>>>
>>> #include<stdio.h>
>>> int main()
>>> {
>>>      short          x = -32768;
>>>      unsigned long  y = -x;
>>>      printf("sizeof short =%d, x=%d, y=%lu\n", sizeof(short), x, y);
>>< }
>>>
>>> As you can see there is no int type used.
>>

>> Yes, there is.  The expression '-x' has a type of "int".  On
>> the MSP430, that's 16 bits and has a value of 0x8000.  When you

>> It's important to note here that "0x8000" for a 16-bit int is -32768.

Yes, that is the source of the problem. One of two.

A look at teh assembly code reveals that the compiler first does a check for 
negative,
then, if x is negative (which it is), it inverts the sign.
This is done by XOR 0xffff nad adding 1.
Unfortunately (0x8000^0xffff)+1 is still 0x8000.

The result of this operation is still signed short int. The compiler assumes 
the value is now positive (any negative value had ben negated).
Next step is the promotion to signed long int.

>> assign that to a long, it gets sign-extended to 0xffff8000.

Indeed. Since the source type hasn't been promoted to unsigned short int by 
abs, the promotion to unsigned long int is with sign extension. And while there 
sould be no sign anymore after abs(), in this special case it 
still is.

>> On the PC, '-x' is 0x00008000, and when assigned to a long,
>> it's still 0x00008000.

>> Don't take this the wrong way, but I think you need a good
>> introductory book on C.


> I don't think this is "introductory" stuff - even very experienced C 
> programmers can get mixed up here.  It took /you/ several posts to pin 
> down the correct explanation, and you are well known for helping out 
> with sticky C questions.

Even if the introductory book surely states, that MAXINT is 32767 ad MININT is 
-32768, it isn't obvious at first glance (and most likely not mentioned) that 
the (short) int range has a larger negative range than positive 
and that the negation of -32768 is still -32768 as this is what comes after 
32767.
Also, it isn't obvious, that in a propagation of signed to unsigned, the source 
is first sign-extended to the bigger size (as it _is_ still signed, no matter 
whether the programmer thinks that any negative sign has been 
removed by abs() ), and then interpreted as unsigned. I'm not sure whether the 
books state that the first conversion step is to bring the source to 
destination size (according to its type with or without sign-extension), 
then cast it to the destination type (where the meaning of the sign might be 
lost). Most books or articles I've read left the impression that typecasting 
(whether the actual size changes or not) is an atomic and instant 
operation.

So while this special case came up because of abs() failing with -32768, the 
conversion problem happen even with -1. A signed short int of -1 cast to an 
unsigned long int will give MAXLONG and not 65535, since first 
the -1 is sign-extended to 32 bit and _then_ cast to unsigned.

OTOH, 

unsigned long  y = (unsigned short)(-x);

would have given the expected result, because here first the type signed short 
had been cast to unsigned short, and THEN expanded to 32 bit without 
sign-extensen (since it is already of unsigned type at this point).

In the above example code, what happened would have been much clearer when the 
printf had used hexadecimal output instead of decimal one. The bit patterns 
would have been obvious.
When noticing strange effects in computed calculations, I always look at the 
hexadecimal patterns and often the source of the problem is suddenly obvious, 
while with decimal numbers, you can search forever.
And, if possible, a look at the assembly code makes things clearer than just 
staring at the debugger :)


> The best advice is to be very careful when dealing with corner cases at 
> the limits of data type sizes, and with promotion between types.  It is 
> better to be explicit than implicit in such cases, and to use 
> size-specific types ("int16" and "uint32" rather than "short" and 
> "unsigned long") - that way you are clear on what you are doing.

Indeed, corner-cases are always a problem that might slip even an experienced 
programmers mind.

I've seen lots of games where the game mechanics failed to work at 
corner-cases. Simple equations for e.g. market reactions to trade fail if 
you're using very high or very low prices/stock counts etc.
Finding such unrecognized corner-cases has made me rich very quickly in some 
games.
But of course it can as well drive a microprocessor program to fail its purpose 
miserably. :)

JMGross


Reply via email to