Compiler Version: Keil C51 5.0
I experienced some unexpected behaviour with the Keil C51 compiler. I have recreated the issue in the below code snippets.
Case 1: Result of bit shift operator is type casted from uchar (8b) to ulong (32b) correctly
unsigned long tempadcVal; unsigned char data_save_high = 0xA4; unsigned char data_save_low = 0x3F; //The expected 32b value without any information loss //Is loaded into tempadcVal tempadcVal = (data_save_high<<2) + (data_save_low>>6);
Case 2: Result of bit shift operator is not type casted from uint (16b) to ulong (32)
unsigned long modulated; unsigned int value = 0xA43F; modulated = value<<8; //Here we have modulated = 0x00003F00 (Wrong!) //Top 8 bits are wiped out
Is this behaviour by design or perhaps a mistake in the compiler?
My guess is I have something wrong in here and someone will point it out (please do if it is even a little bit helpful). By my evaluation, what happens is 100% correct for the C language.
unsigned long tempadcVal; unsigned char data_save_high = 0xA4; unsigned char data_save_low = 0x3F; //The expected 32b value without any information loss //Is loaded into tempadcVal tempadcVal = (data_save_high<<2) + (data_save_low>>6); //** the value of data_save_high is typecast to an int and then shifted (2 is an int) //** the value of data_save_low is typecast to an int and then shifted ( 6 is an int) //** both ints are added together and the resulting int is typecast to an unsigned long
Case 2: Result of bit shift operator is not typecast from uint (16b) to ulong (32)
unsigned long modulated; unsigned int value = 0xA43F; modulated = value<<8; //** value is not typecast at all since it will not fit in an int //** when shifted value == 0x3F00 (as I believe it should be) //** unsigned int result is typecast to an unsigned long at this point. (0x00003F00) //Here we have modulated = 0x00003F00 (Wrong!) //Top 8 bits are wiped out Is this behaviour by design or perhaps a mistake in the compiler?
So, Bob McNamara, are you saying that the standard C behaviour is for int to be the special 'default case' data type and variables are typecasted by default to the int data type only?
No. That is not what I was trying to say at all, nor is that what is happening. There are rules for what types are used for arithmetic operations. The types of the operands will be the same and any "lower type" will be converted to the "higher type"
712 If both operands have the same type, then no further conversion is needed.
713 Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.
714 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.
715 Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.
716 Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.
unsigned long tempadcVal; unsigned char data_save_high = 0xA4; unsigned char data_save_low = 0x3F; //The expected 32b value without any information loss //Is loaded into tempadcVal tempadcVal = (data_save_high<<2) + (data_save_low>>6); //***** "<<" is an arithmetic operator. //***** data_save_high is an unsigned character //***** 2 is an integer //***** data_save_high must be converted to an integer before the arithmetic operation is performed ( 713 above) //** the value of data_save_high is typecast to an int and then shifted (2 is an int) //** the value of data_save_low is typecast to an int and then shifted ( 6 is an int) //** both ints are added together and the resulting int is typecast to an unsigned long
unsigned long modulated; unsigned int value = 0xA43F; modulated = value<<8; //***** value is not typecast at all since it will not fit in an int //***** 8 is an integer and will be cast to an unsigned int //***** the << operator is acting on 2 unsigned int's (16-bits) //***** when you shift the 16-bits 8 bits to the left, you get 0x3F00 //***** After this is done, you then you assign the unsigned int to an unsigned long int, but you have already lost the upper 16-bits. //** when shifted value == 0x3F00 (as I believe it should be) //** unsigned int result is typecast to an unsigned long at this point. (0x00003F00)
//Here we have modulated = 0x00003F00 (Wrong!) //Top 8 bits are wiped out Is this behaviour by design or perhaps a mistake in the compiler?
Result of bit shift operator is type casted
All explanations so far containing that partial phrase are wrong, because they try to explain the wrong aspect of the actual behaviour.
The issue at hand is not with converting the results of shift operations, but rather with converting their left operands. The bits that get lost do so because the sub-integer operands only get auto-converted up to (unsigned) int, then the shift happens, and the result is still only *unsigned) int. The bit that got lost did so in the shift itself, because (unsigned) int is only 16 bits on this platform.
By the time the result of the shift is converted (again), all the "missing" bits are already gone.
Thank you, Bob McNamara, for the explanation. Now I think, I correctly understand the results of the above operations. Thanks, once again for clearing that up. -Ishank
Thank you, Hans-Bernhard Broeker, for pointing that out, I think Bob's answer helped me understand how that statement is incorrect. -Ishank