web-dev-qa-db-ja.com

8つのブール値からバイトを作成する方法(およびその逆)?

8つのbool変数があり、それらを1バイトに「マージ」したいと思います。

これを行うための簡単な/好ましい方法はありますか?

逆に、バイトを8つの別々のブール値にデコードするのはどうですか?

不合理な質問ではないと思いますが、グーグルで関連資料が見つからなかったので、「直観が間違っている」というケースのひとつかもしれません。

22
xcel

難しい方法:

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のビットの順序は実装によって定義されていることに注意してください。

21
rodrigo

あなたは調べたいかもしれません std::bitset 。期待するすべての演算子を使用して、ブール値をビットとしてコンパクトに格納できます。

あなたが抽象化できるときは、ビットフリッピングなどでだまされても意味がありません。

12
Lalaland
#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);
}
5
Jeremy Friesner

クールな方法( 乗算手法 を使用)

_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つの連続するboolsを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 で非常に遅いので、どちらが優れているかを確認するために上記の乗算方法と比較する必要があるかもしれません

5
phuclv
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

おそらくビットセットを使用したほうがいいでしょうが

http://www.cplusplus.com/reference/stl/bitset/bitset/

2
111111

8つのbool変数を1バイトにパックする方法はありません。 Bitmasking を使用して、8つの論理的なtrue/false状態を1バイトにパックする方法があります。

1
ScarletAmaranth

unionsを介した型のパンニングは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が型のパンニングを行う方法です。

1
iBug

ビット単位のシフト演算とキャストを使用してアーカイブします。関数は次のように機能します。

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