6

I have an unsigned char array with 2 elements that represents a signed integer. How can I convert these 2 bytes into a signed integer?

Edit: The unsigned char array is in little endian

2
  • You gotta watch out for endiannes, see en.wikipedia.org/wiki/Endianness Commented Jan 24, 2011 at 13:16
  • There are several ways in which two unsigned char could represent a signed integer. You should be more specific on that. Commented Jan 24, 2011 at 13:23

4 Answers 4

6

For maximum safety, use

int i = *(signed char *)(&c[0]);
i *= 1 << CHAR_BIT;
i |= c[1];

for big endian. Swap c[0] and c[1] for little endian.

(Explanation: we interpret the byte at c[0] as a signed char, then arithmetically left shift it in a portable way, then add in c[1].)

Sign up to request clarification or add additional context in comments.

5 Comments

As I told after, if you just cast the unsigned char to signed char you risk the sign bit to be expanded. Better mask the result with 0x0000FFFF to be sure the sign bit is low.
@Mr.Gate, I'm not "just" casting unsigned char to signed char. I'm using a pointer cast to reinterpret the bit pattern as signed char, then promoting to int.
Most compilers do not ignore aliasing rules. GCC will almost surely generate code that fails to do what you want unless you pass it special options.
the (int16_t *)c may induce SIGBUS on many RISC processor, and reduce performance on IA86 architecture.
@R..: You're right. I thought I had to turn it on explicitly in GCC, but it's implied in -O2. Removed the pointer trick.
5

wrap them up in a union:

union {
  unsigned char a[2];
  int16_t smt;
} number;

Now after filling the array you can use this as number.smt

1 Comment

@VGE: short to int conversion is well-defined. Though I'd recommend using int16_t instead of short.
1

It depend of endianness. Something for big endian :

unsigned char x[2];
short y = (x[0] << 8) | x[1]

Something for little endian :

unsigned char x[2];
short y = (x[1] << 8) | x[0]

1 Comment

This is not likely to work on many systems, particularly where plain char is signed (x[0] and x[1] will be sign-extended, so if the least significant one is negative, the upper byte will be set to all 1s by the bitwise OR).
1

The portable solution:

unsigned char c[2];
long tmp;
int result;

tmp = (long)c[0] << 8 | c[1];
if (tmp < 32768)
    result = tmp;
else
    result = tmp - 65536;

This assumes that the bytes in the array represent a 16 bit, two's complement, big endian signed integer. If they are a little endian integer, just swap c[1] and c[0].

(In the highly unlikely case that it is ones' complement, use 65535 instead of 65536 as the value to subtract. Sign-magnitude is left as an exercise for the reader ;)

2 Comments

I'd rather mask the result with 0x0000FFFF. This ensures that, even if sign bit expanded, you will grant the valute to be within 0x0000FFFF and 0.
@Mr.Gate: There is no need for any masking. Since c[0] and c[1] are positive numbers between 0 and 255, tmp is a positive number in range 0 to 65535. The only negative number is produced in the final subtraction - there is no chance of unwanted sign extension.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.