How do I combine 4 2-bit values into 1 8-bit value?
An answer to this question on Stack Overflow.
Question
This code is what I am using to combine 4 2-bit values (unsigned chars but they only hold values from 0-3) into 1 single unsigned char value
unsigned char nibble = 0;
nibble = (nibble & 0x03) | (output[i] & 0x03);
nibble = (nibble & 0x0C) | (output[i+1] & 0x03) << 2);
nibble = (nibble & 0x30) | (output[i+2] & 0x03) << 4);
nibble = (nibble & 0xC0) | (output[i+3] & 0x03) << 6);
It produces an incorrect value for everything except 00 00 00 00 (it often produces the same result for 2 different sets of 2-bit values).
I'm confused because the code above is an edit of this code, which works fine to combine 2 4-bit values into 1 byte, so why does my version not combine 4 2-bit values into 1 byte?
char byte;
byte = (byte & 0xF0) | (nibble1 & 0xF); // write low quartet
byte = (byte & 0x0F) | ((nibble2 & 0xF) << 4); // write high quartet
I've tried changing 0x03 / 0x0C / 0x30 / 0xC0 to 0xC0 / 0x30 / 0x0C / 0x03, still wrong. Same for changing & 0x03 to & 0xC0.
Answer
It's because you clear bits in the nibble each time.
That is, when you say this:
nibble = (nibble & 0xC0)
what you are really saying is "throw away all the work I've done so far, except for the bits at positions 3 and 4".
This code (untested) will probably solve your problem:
unsigned char nibble = 0;
nibble |= (output[i ] & 0x03);
nibble |= (output[i+1] & 0x03) << 2;
nibble |= (output[i+2] & 0x03) << 4;
nibble |= (output[i+3] & 0x03) << 6;
If it's really true that output[i+x] only holds values in the range [0,3], then you can change the code as follows:
unsigned char nibble = 0;
assert(0<=output[i ] && output[i ]<=3)
nibble |= output[i ];
assert(0<=output[i+1] && output[i+1]<=3)
nibble |= output[i+1] << 2;
assert(0<=output[i+2] && output[i+2]<=3)
nibble |= output[i+2] << 4;
assert(0<=output[i+3] && output[i+3]<=3)
nibble |= output[i+3] << 6;
The asserts could, of course, be removed if you're really, really sure. But you can also leave them in and have the compiler extirpate them by using the NDEBUG flag (g++ -DNDEBUG mycode.cpp). See this question for further details.