Home > front end >  Correct interpretation signed vs unsigned
Correct interpretation signed vs unsigned

Time:12-21

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.

  • Related