如何将4个2位值组合成1个8位值?

时间:2017-08-10 01:52:50

标签: c++ c bit-manipulation bit bit-shift

我正在使用这段代码将4个2位值(无符号字符但它们只保存0-3的值)组合成1个单个无符号字符值

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);

它会为除00 00 00 00之外的所有内容生成不正确的值(对于2个不同的2位值,它通常会产生相同的结果)。

我很困惑,因为上面的代码是this code的编辑,可以很好地将2个4位值组合成1个字节,那么为什么我的版本不会将4个2位值组合成1个字节?

char byte;
byte = (byte & 0xF0) | (nibble1 & 0xF); // write low quartet
byte = (byte & 0x0F) | ((nibble2 & 0xF) << 4); // write high quartet

我尝试将0x03 / 0x0C / 0x30 / 0xC0更改为0xC0 / 0x30 / 0x0C / 0x03,仍然是错误的。改变和改变相同0x03到&amp;将0xC0。

2 个答案:

答案 0 :(得分:5)

这是因为你每次都清除半字节中的位。

也就是说,当你这么说时:

nibble = (nibble & 0xC0)

你真正说的是“扔掉我到目前为止所做的所有工作,除了第3和第4位的位”。

此代码(未经测试)可能会解决您的问题:

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;

如果真的为真,output[i+x]只保留 [0,3] 范围内的值,那么您可以按如下方式更改代码:

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;

当然,如果你真的非常肯定的话,可以删除断言。但您也可以将它们保留在内,并让编译器使用NDEBUG标志(g++ -DNDEBUG mycode.cpp)来删除它们。有关详细信息,请参阅this question

答案 1 :(得分:0)

那是因为在使用新位填充nibble之前,您使用掩码过滤它,如果有任何与它应该做的相反的话。

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

反转该掩码可行:

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);

但是,由于nibble已经以0开头,因此您不需要这样做。只需填充Richard's answer中的位。