Reputation: 903
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.
Upvotes: 0
Views: 1978
Reputation: 61499
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.
Upvotes: 5
Reputation: 27934
That's because before populating nibble
with the new bits you are filtering it with a mask that if anything does the opposite of what it should do.
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);
^^^^ in here you preserve the bits
you want to replace and zero out everything else
Inverting that mask would work:
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);
However, since nibble
already starts as 0
anyway, you don't need that. Just populate the bits as in Richard's answer.
Upvotes: 0