8つのbool
変数があり、それらを1バイトに「マージ」したいと思います。
これを行うための簡単な/好ましい方法はありますか?
逆に、バイトを8つの別々のブール値にデコードするのはどうですか?
不合理な質問ではないと思いますが、グーグルで関連資料が見つからなかったので、「直観が間違っている」というケースのひとつかもしれません。
難しい方法:
unsigned char ToByte(bool b[8])
{
unsigned char c = 0;
for (int i=0; i < 8; ++i)
if (b[i])
c |= 1 << i;
return c;
}
そして:
void FromByte(unsigned char c, bool b[8])
{
for (int i=0; i < 8; ++i)
b[i] = (c & (1<<i)) != 0;
}
またはクールな方法:
struct Bits
{
unsigned b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1;
};
union CBits
{
Bits bits;
unsigned char byte;
};
次に、ユニオンの1つのメンバーに割り当てて、別のメンバーから読み取ることができます。ただし、Bits
のビットの順序は実装によって定義されていることに注意してください。
あなたは調べたいかもしれません std::bitset
。期待するすべての演算子を使用して、ブール値をビットとしてコンパクトに格納できます。
あなたが抽象化できるときは、ビットフリッピングなどでだまされても意味がありません。
#include <stdint.h> // to get the uint8_t type
uint8_t GetByteFromBools(const bool eightBools[8])
{
uint8_t ret = 0;
for (int i=0; i<8; i++) if (eightBools[i] == true) ret |= (1<<i);
return ret;
}
void DecodeByteIntoEightBools(uint8_t theByte, bool eightBools[8])
{
for (int i=0; i<8; i++) eightBools[i] = ((theByte & (1<<i)) != 0);
}
_inline uint8_t pack8bools(bool* a)
{
uint64_t t = *((uint64_t*)a);
return 0x8040201008040201*t >> 56;
}
void unpack8bools(uint8_t b, bool* a)
{
auto MAGIC = 0x8040201008040201ULL;
auto MASK = 0x8080808080808080ULL;
*((uint64_t*)a) = ((MAGIC*b) & MASK) >> 7;
}
_
sizeof(bool) == 1
と仮定します
もちろん、パフォーマンスの低下やUBを回避するために、bool配列が正しく8バイトに整列されていることを確認する必要がある場合があります。
_b[0]
_から_b[7]
_までの8つのブールがあり、その最下位ビットにはそれぞれa-hという名前が付けられており、1バイトにパックするとします。これらの8つの連続するbool
sを1つの64ビットワードとして扱い、それらをロードすると、リトルエンディアンのマシンでビットが逆の順序で取得されます。次に、乗算を実行します(ここではドットはゼロビットです)
_ | b7 || b6 || b4 || b4 || b3 || b2 || b1 || b0 |
.......h.......g.......f.......e.......d.......c.......b.......a
× 1000000001000000001000000001000000001000000001000000001000000001
────────────────────────────────────────────────────────────────
↑......h.↑.....g..↑....f...↑...e....↑..d.....↑.c......↑b.......a
↑.....g..↑....f...↑...e....↑..d.....↑.c......↑b.......a
↑....f...↑...e....↑..d.....↑.c......↑b.......a
+ ↑...e....↑..d.....↑.c......↑b.......a
↑..d.....↑.c......↑b.......a
↑.c......↑b.......a
↑b.......a
a
────────────────────────────────────────────────────────────────
= abcdefghxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
_
マジックナンバーの設定ビットの位置がわかりやすいように矢印が追加されています。この時点で、最下位8ビットが最上位バイトに配置されています。残りのビットをマスクする必要があります。
したがって、パッキングのマジックナンバーは_0b1000000001000000001000000001000000001000000001000000001000000001
_または_0x8040201008040201
_になります。ビッグエンディアンマシンを使用している場合は、同様の方法で計算されるマジックナンバー_0x0102040810204080
_を使用する必要があります。
開梱についても同様の掛け算ができます
_ | b7 || b6 || b4 || b4 || b3 || b2 || b1 || b0 |
abcdefgh
× 1000000001000000001000000001000000001000000001000000001000000001
────────────────────────────────────────────────────────────────
= h0abcdefgh0abcdefgh0abcdefgh0abcdefgh0abcdefgh0abcdefgh0abcdefgh
& 1000000010000000100000001000000010000000100000001000000010000000
────────────────────────────────────────────────────────────────
= h0000000g0000000f0000000e0000000d0000000c0000000b0000000a0000000
_
乗算した後、最上位の位置に必要なビットがあるため、無関係なビットをマスクして、残りのビットを最下位の位置にシフトする必要があります。出力は、リトルエンディアンのaからhを含むバイトになります。
BMI2 を備えた新しいx86 CPUには、 [〜#〜] pext [〜#〜] および [〜#〜] pdep [〜#〜] この目的のための命令。上記の_pack8bools
_関数は次のように置き換えることができます
__pext_u64(*((uint64_t*)a), 0x0101010101010101ULL);
_
そして、_unpack8bools
_関数は次のように実装できます。
__pdep_u64(b, 0x0101010101010101ULL);
_
残念ながら これらの命令はAMD で非常に遅いので、どちらが優れているかを確認するために上記の乗算方法と比較する必要があるかもしれません
bool a,b,c,d,e,f,g,h;
//do stuff
char y= a<<7 | b<<6 | c<<5 | d<<4 | e <<3 | f<<2 | g<<1 | h;//merge
おそらくビットセットを使用したほうがいいでしょうが
8つのbool
変数を1バイトにパックする方法はありません。 Bitmasking を使用して、8つの論理的なtrue/false状態を1バイトにパックする方法があります。
union
sを介した型のパンニングはC++ではUBであることに注意してください(rodrigoが 彼の答え)で行うように 。これを行う最も安全な方法はmemcpy()
です。
_struct Bits
{
unsigned b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1;
};
unsigned char toByte(Bits b){
unsigned char ret;
memcpy(&ret, &b, 1);
return ret;
}
_
他の人が言っているように、コンパイラはmemcpy()
を最適化するのに十分賢いです。
ところで、これはBoostが型のパンニングを行う方法です。
ビット単位のシフト演算とキャストを使用してアーカイブします。関数は次のように機能します。
unsigned char toByte(bool *bools)
{
unsigned char byte = \0;
for(int i = 0; i < 8; ++i) byte |= ((unsigned char) bools[i]) << i;
return byte;
}
ありがとう Christian Ra 訂正してくれてs!