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?
The compiler does its maths as int (16 bit) so shuffling the 0xA43F by 8 left will produce 0x3F00. That 16 bit result is then promoted to a 32 bit value.
If you want to keep the detail, you need to ensure the maths is done as 32 bit, such as:
modulated = ((unsigned long) value) << 8;
Indeed.
That's actually a standard 'C' thing - not specific to Keil or C51.
Thank you FEx for your reply.
I completely understand the need for explicit type casting here and the reasons for the second case not working as expected.
However, what I think the problem is the inconsistency between the first and second case of the original post.
I have tried this again in the Keil C compiler and it seems that unsigned char gets cast to a uint or an unsigned long without the need for any typecasting. (I have also observed this in examples other than the one I have posted here)
However when it comes to casting uint to larger data types, this process is not implicit and as you have rightly pointed out, typecasting is required.
-Ishank
Andrew, thank you for taking the time to read the original post. COuld you also read the follow up post I have written as a reply to @F Ex?
Thanks again
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