More bitwise woes

J

Johnathan Doe

I am trying to do some bit fiddling with unsigned chars. Don't ask me
why I am doing this, I don't know. It's just an exercise. :)

Here's what I am trying to do: I have one unsigned char of a certain
pattern, and four unsigned chars all with the bit pattern 010 00 010.
The first two binary bits leftmost of the first will go in the middle of
the first char with the bit pattern 010 00 010. The second lot of bits
from the first char will go in the middle of the second char with the
bit pattern 010 00 010. And so on for bits 2, 3 and bits 0, 1 of the
first char.

So, here are the chars with the 010 00 010 patterns.

unsigned char finished[4];
finished[0] |= (0x02 << 5);
finished[0] |= 0x02;

.... and so on for finished[1-3].

And now I have a char with whatever in it, say 'H'.

unsigned char ch = 'H';

Now, I want the leftmost two bits in the middle of finished[0]. What I
attempt to do is OR ch with 0xC0, which gets me just the two leftmost
bits. Then shift them 3 places to the right, to get them in the middle
of the 8-bit char. Then OR with 0x18, which gets me just the two middle
bits, which were previously the leftmost two bits before they were
shifted down into the middle:

finished[0] |= (3 >> (0xC0 & ch)) & 0x18;

Now for finished[1], which has the same 010 00 010 bit pattern, and the
next two leftmost bits of ch, which are bits 4 and 5 (counting from 0
from the left).

This attempts to get a mask for just bits 4 and 5 in ch, shifts them one
place down to the right to get them in the middle, then gets those two
middle bits:

finished[1] |= (1 >> (0x30 & ch)) & 0x18;

Now for finished[2]:

finished[2] |= ((0x0C & ch) << 1) & 0x18;

And for finished[3]:

finished[3] |= ((0x03 & ch) << 3) & 0x18;

Problem is I can't make it work. Am I doing something wrong?

Thanks for your help.

Johnathan
 
S

Sascha Springer

finished[0] |= (0xC0 & ch) >> 3;
finished[1] |= (0x30 & ch) >> 1;
finished[2] |= (0x0C & ch) << 1;
finished[3] |= (0x03 & ch) << 3;

alternatively:

finished[0] |= (ch >> 3) & 0x18;
finished[1] |= (ch >> 1) & 0x18;
finished[2] |= (ch << 1) & 0x18;
finished[3] |= (ch << 3) & 0x18;

Regards
Sasca
 
J

Jens.Toerring

Johnathan Doe said:
I am trying to do some bit fiddling with unsigned chars. Don't ask me
why I am doing this, I don't know. It's just an exercise. :)
Here's what I am trying to do: I have one unsigned char of a certain
pattern, and four unsigned chars all with the bit pattern 010 00 010.
The first two binary bits leftmost of the first will go in the middle of
the first char with the bit pattern 010 00 010. The second lot of bits
from the first char will go in the middle of the second char with the
bit pattern 010 00 010. And so on for bits 2, 3 and bits 0, 1 of the
first char.
So, here are the chars with the 010 00 010 patterns.
unsigned char finished[4];
finished[0] |= (0x02 << 5);

The '|=' here is dangerous since - at least the way you show it here -
the elements of 'finished' probably aren't initialized, so finished[0]
could be some random value to which you 'or' 0x40.
finished[0] |= 0x02;

Why not make that

finished[0] = 0x02 << 5 | 0x02;

or just

finished[0] = 0x42;
... and so on for finished[1-3].

I don't understand why you do all that, to me it looks like an overly
complicated way of just having

unsigned char finished[4] = { 0x42, 0x42, 0x42, 0x42 };
And now I have a char with whatever in it, say 'H'.
unsigned char ch = 'H';
Now, I want the leftmost two bits in the middle of finished[0]. What I
attempt to do is OR ch with 0xC0, which gets me just the two leftmost
bits. Then shift them 3 places to the right, to get them in the middle
of the 8-bit char. Then OR with 0x18, which gets me just the two middle
bits, which were previously the leftmost two bits before they were
shifted down into the middle:
finished[0] |= (3 >> (0xC0 & ch)) & 0x18;

Here you are shifting the bits of the value 3 to the right - with both
'<<' and '>>' the value to be shifted is on the left of the operator,
the number of bits to shift on the right. But in the text you write you
want to shift the upper two bits to the right by 3 positions. So

finished[0] |= ( ch & 0xC0 ) >> 3;

is what you need - you can drop the "& 0x18" part since the "& 0xC0"
made already sure only the two bits can be set.
Now for finished[1], which has the same 010 00 010 bit pattern, and the
next two leftmost bits of ch, which are bits 4 and 5 (counting from 0
from the left).
This attempts to get a mask for just bits 4 and 5 in ch, shifts them one
place down to the right to get them in the middle, then gets those two
middle bits:
finished[1] |= (1 >> (0x30 & ch)) & 0x18;

Same problem here

finished[1] |= ( ch & 0x30 ) >> 1;
Now for finished[2]:
finished[2] |= ((0x0C & ch) << 1) & 0x18;

This looks ok (but you can also drop the "& 0x18" part here).
And for finished[3]:
finished[3] |= ((0x03 & ch) << 3) & 0x18;

Ditto.
Regards, Jens
 
J

Johnathan Doe

Thankyou thankyou thankyou, Sasca and Jens!!

This has stumped me for days! In my 5 years of C programming the only
thing I never "got" was bitwise stuff. Now I am finally starting to
understand it.

Thanks for your help. :)
 
K

kevin.bagust

Johnathan Doe
I am trying to do some bit fiddling with unsigned chars. Don't ask me
why I am doing this, I don't know. It's just an exercise. :)
Here's what I am trying to do: I have one unsigned char of a certain
pattern, and four unsigned chars all with the bit pattern 010 00 010.
The first two binary bits leftmost of the first will go in the middle of
the first char with the bit pattern 010 00 010. The second lot of bits
from the first char will go in the middle of the second char with the
bit pattern 010 00 010. And so on for bits 2, 3 and bits 0, 1 of the
first char.

If I read this correctly, given a byte with the form:
b0 b1 b2 b3 b4 b5 b6 b7

you want to end up with the series of bytes:

0: 010 b0 b1 010
1: 010 b2 b3 010
2: 010 b4 b5 010
3: 010 b6 b7 010
So, here are the chars with the 010 00 010 patterns.
unsigned char finished[4];
finished[0] |= (0x02 << 5);
finished[0] |= 0x02;
... and so on for finished[1-3].
And now I have a char with whatever in it, say 'H'.
unsigned char ch = 'H';
Now, I want the leftmost two bits in the middle of finished[0]. What I
attempt to do is OR ch with 0xC0, which gets me just the two leftmost
bits. Then shift them 3 places to the right, to get them in the middle
of the 8-bit char. Then OR with 0x18, which gets me just the two middle
bits, which were previously the leftmost two bits before they were
shifted down into the middle:
finished[0] |= (3 >> (0xC0 & ch)) & 0x18;

You are saying OR in the text but then anding in the code. Also you have
the values the wrong way round for the shift, the value to shift is first
then the shift sign then the amount to shift by.

What you need to do (I think) is to shift the value of ch right by 3 bits,
then AND the value with 0x18. Which gives you the code:

finished[ 0 ] |= ( ch >> 3 ) & 0x18;

then for the other three bytes you just need to alter the direction and
sive of the shift.

Hope this helps.

Kevin.
 
J

Johnathan Doe

kevin.bagust said:
If I read this correctly, given a byte with the form:
b0 b1 b2 b3 b4 b5 b6 b7

you want to end up with the series of bytes:

0: 010 b0 b1 010
1: 010 b2 b3 010
2: 010 b4 b5 010
3: 010 b6 b7 010

That's correct, yes.
You are saying OR in the text but then anding in the code. Also you have
the values the wrong way round for the shift, the value to shift is first
then the shift sign then the amount to shift by.

What you need to do (I think) is to shift the value of ch right by 3 bits,
then AND the value with 0x18. Which gives you the code:

finished[ 0 ] |= ( ch >> 3 ) & 0x18;

then for the other three bytes you just need to alter the direction and
sive of the shift.

Hope this helps.

It does, thanks heaps. :)

I was confused on the issue of what gets shifted and what is how much to
shift by. I had them round the wrong way and all sorts! And didn't
need the final mask since I had already gotten the bits I was interested
in, etc.

Cheers
Johnathan
 
C

Charlie Gordon

If I read this correctly, given a byte with the form:
b0 b1 b2 b3 b4 b5 b6 b7

you want to end up with the series of bytes:

0: 010 b0 b1 010
1: 010 b2 b3 010
2: 010 b4 b5 010
3: 010 b6 b7 010

It is a bit misleading to number bits this way.

Even though it is merely a matter of conventions, I find much more consistent to
number bits from the least significant to the most significant starting at 0.
It is more consistent because it makes the value of bx be 1<<x or 2 to the x.
Most hardware vendors use this convention. IBM is among the few that use the
opposite convention, starting at 1, creating much confusion among hardware
specification readers.

The character in the OP's question would then be :
b7 b6 b5 b4 b3 b2 b1 b0

and the intended result:

0: 0 1 0 b7 b6 0 1 0
1: 0 1 0 b5 b4 0 1 0
2: 0 1 0 b3 b2 0 1 0
3: 0 1 0 b1 b0 0 1 0
 
C

Chris Croughton

Even though it is merely a matter of conventions, I find much more consistent to
number bits from the least significant to the most significant starting at 0.
It is more consistent because it makes the value of bx be 1<<x or 2 to the x.
Most hardware vendors use this convention. IBM is among the few that use the
opposite convention, starting at 1, creating much confusion among hardware
specification readers.

Several of the European telcommunications specifications start with bit
0 as the high bit. I believe they were written by the French (this
explains a lot of weird things)...
The character in the OP's question would then be :
b7 b6 b5 b4 b3 b2 b1 b0

and the intended result:

0: 0 1 0 b7 b6 0 1 0
1: 0 1 0 b5 b4 0 1 0
2: 0 1 0 b3 b2 0 1 0
3: 0 1 0 b1 b0 0 1 0

I find that a lot more logical as well...

Chris C
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,999
Messages
2,570,243
Members
46,836
Latest member
login dogas

Latest Threads

Top