I'm receiving 2 bytes from a communication and then i need to merge these 2 values to obtain a 16 bit value.
Now suppose that I expect to receive the number 200, then the two characters are a and b
char a=0x00;
char b=0xc8;
int cw = (a << 8) | b ;
printf("cw= %d\n",cw);
Doing the merge the variable cw becomes -56 instead of 200
If i change char by unsigned char i got the correct value 200 How can i fix that? i expect to receive both positive and negative numbers and of course the number 200
CodePudding user response:
16 bit value.
Just use proper type.
unsigned char a = 0x00;
unsigned char b = 0xc8;
int16_t cw = ((unsigned int)a << 8) | b;
CodePudding user response:
The C standard essentially provides no way to shift a 1 bit into or out of the sign position (the only defined cases for <<
are for non-negative values that do not overflow) and no definite way to convert an unsigned value to a negative value (conversions of out-of-range values to signed integer types are implementation-defined).
So we should not use a shift. However, multiplication of negative values is of course defined, so we can use:
int8_t a;
uint8_t b;
// Put code here to receive a and b by some method.
uint16_t cw = a*256 b;
Another option is to test the sign bit and apply the two’s complement manually:
unsigned char a, b;
// Put code here to receive a and b by some method.
int cw = (a & 0x7f) << 8 | b; // Assemble the low 15 bits.
if (a & 0x80)
cw -= 0x8000; // If sign bit is set, adjust.
We can also copy the bits in:
unsigned char a, b;
// Put code here to receive a and b by some method.
int16_t cw;
memcpy(&cw, (uint16_t []) { (uint16_t) a << 8 | b }, sizeof cw);
(The above presume your 16-bit integer uses two’s complement.)
CodePudding user response:
The only change you should do is defining the least significant byte unsigned:
char a;
unsigned char b;
... // receive a and b from your communication
int cw = (a << 8) | b;
printf("cw = %d\n", cw);
The arithmetic/logic expression should work, but it may be non-trivial to explain why it doesn't overflow, because it involves promotion of char
or unsigned char
to int
(which, I guess, is 16-bit on your system).
If you want your code to be portable (i.e. not assume 16-bit int
or any other property of your specific platform), use integers with defined size, and do explicit casting.
int8_t a;
uint8_t b;
... // receive a and b from your communication
int16_t cw = (int16_t)((int16_t)a << 8) | (int16_t)b;
printf("cw = %d\n", (int)cw);
But this code is less readable, so I am not sure being more portable has any advantage here.