简体   繁体   中英

How do I combine 4 2-bit values into 1 8-bit value?

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.

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.

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM