1

I have:

(gdb) display/t raw_data[4]<<8 24: /t raw_data[4]<<8 = 1111100000000 (gdb) display/t raw_data[5] 25: /t raw_data[5] = 11100111 (gdb) display/t (raw_data[4]<<8)|raw_data[5] 26: /t (raw_data[4]<<8)|raw_data[5] = 11111111111111111111111111100111 

Why is the result on line 26 not 0001111111100111? Thanks.

edit: More specifically:

(gdb) display/t raw_data[5] 27: /t raw_data[5] = 11100111 (gdb) display/t 0|raw_data[5] 28: /t 0|raw_data[5] = 11111111111111111111111111100111 

Why is the result on line 26 not 11100111?

2
  • Machine endianness has nothing to do with bit shifting of native integer types. Care to tell us what type raw_data is. There is very likely a signed/unsigned promotion at work here. Commented Jun 6, 2014 at 16:38
  • char*raw_data is the type Commented Jun 6, 2014 at 16:39

3 Answers 3

6

Your data type is a char, which on your platform appears to be signed. The entry raw_data[5] holds the negative number -25.

The print format t prints the data as unsigned integer in binary. When you print raw_data[5], it is converted to the unsigned char 213, but has only 8 bits. When you do the integer arithmetic on the data, the chars are promoted to a 32-bit integer.

Promoting the negative char value -25 to a signed int will, of course, yield -25, but its representation as an unsigned int is now 2^^32 + x, whereas as an unsigned char it was 2^^8 + x. That's where all the ones at the beginning of the 32-bit binary number come from.

It's maybe better to work with unsigned raw data.

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

4 Comments

Suggestions on casting to get the results I expect? raw_data[4]<<UINT8_C(8) | raw_data[5]?
Casting helps, but you have to do it on the shifted value, not on the shift amount. I suggest working with unsogned chars throughout, i.e. make raw_data an unsigned type, at least locally. Saves you a lot of worries.
decided to just go with raw_data[4]<< 8 | (raw_data[5] & 0x00ff);
@TheoretiCAL: "It's maybe better to work with unsigned [char] data." This sounds like very good advice to me
2

Let's just ignore the first block, since the second block is a minimal reproduction.

Also note that 0 | x preserves the value of x, but causes the usual integral promotions.

Then the second block is not so unexpected.

(gdb) display/t raw_data[5] 27: /t raw_data[5] = 11100111 

Ok, raw_data[5] is int8_t(-25)

(gdb) display/t 0|raw_data[5] 28: /t 0|raw_data[5] = 11111111111111111111111111100111 

and 0|raw_data[5] is int(-25). Indeed, the value was preserved.

Comments

0

The constant 8 caused a promotion to a signed integer, so you're seeing sign extension as well at the promotion. Change it to UINT8_C(8). You'll need to include stdint.h for the macro.

2 Comments

Added #include <stdint.h> but UINT8_C errors on compilation for not being defined in the scope, any ideas?
What compiler are you using? UINT8_C is the proper cross platform way of doing it, but you could just use 8U, which would work on just about everything anyway.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.