views:

257

answers:

2

Hi all, I discovered something odd that I can't explain. If someone here can see what or why this is happening I'd like to know. What I'm doing is taking an unsigned short containing 12 bits aligned high like this:

1111 1111 1111 0000

I then want to shif the bits so that each byte in the short hold 7bits with the MSB as a pad. The result on what's presented above should look like this:

0111 1111 0111 1100

What I have done is this:

unsigned short buf = 0xfff;
//align high
buf <<= 4;

buf >>= 1;
*((char*)&buf) >>= 1;

This gives me something like looks like it's correct but the result of the last shift leaves the bit set like this:

0111 1111 1111 1100

Very odd. If I use an unsigned char as a temporary storage and shift that then it works, like this:

unsigned short buf = 0xfff;
buf <<= 4;

buf >>= 1;
tmp = *((char*)&buf);
*((char*)&buf) = tmp >> 1;

The result of this is:

0111 1111 0111 1100

Any ideas what is going on here?

+4  A: 

Yes, it looks like char is signed on your platform. If you did *((unsigned char*)&buf) >>= 1, it would work.

Keith Randall
Yes! Thank you for the explanation, it's working now. It was driving me mad.
foo
+1  A: 

Lets break this down. I'll assume that your compiler thinks of short as a 16-bits of memory.

unsigned short buf = 0xfff; 
//align high 
buf <<= 4; 

is equivalent to:

unsigned short buf = 0xfff0;

... and

buf >>= 1; 

should result in buf having the value 0x7ff8 (i.e. th bits shifted to the right by one bit). Now for your fancy line:

*((char*)&buf) >>= 1; 

lots going on here... first the left hand side needs to be resolved. What you're saying is take buf and treat it as a pointer to 8-bits of memory (as opposed to it's natural 16-bits). Which of the two bytes that buf originally referred to relies on knowning what your memory endian-ness is (if it's big-endian buf points to 0x7f, if it's little-endian buf points to 0xf8). I'll assume you're on an Intel box, which means its little endian, and now buff points to 0xf8. Your statement then says assign to that byte, the value at that byte shifted (and sign extended since char is signed) to the right by one, or 0xfc. The other byte will remain unchanged. If you want no sign extension, chast buf to a (unsigned char *).

vicatcu
Thanks. It was the omission of `unsigned` that got me :D. Frustrating.
foo