2011-07-02 5 views
23

低レベルのビット操作は決して私の強みではありません。私はこのコードを達成し、どのようにされるかわからないよ...複数の値を1つのintにパックするためのビット演算子の使用

int age, gender, height, packed_info; 

. . . // Assign values 

// Pack as AAAAAAA G HHHHHHH using shifts and "or" 
packed_info = (age << 8) | (gender << 7) | height; 

// Unpack with shifts and masking using "and" 
height = packed_info & 0x7F; // This constant is binary ...01111111 
gender = (packed_info >> 7) & 1; 
age = (packed_info >> 8); 

をビット単位operators.Considerの次のユースケースを理解する上でいくつかの助けに感謝しますか!なぜマジックナンバー0x7Fを使うのですか?梱包と開梱はどのように行われますか?

Source

+11

を訪問し、私はそれがこの質問をする前に進数表現とビット演算子について読んで価値があると思います。 –

+7

コメントの画像はほとんどすべてそれを言っています:AAAAAAA G HHHHHHH –

答えて

54

として、我々は形式で、15ビットに、年齢、性別、身長をパックするつもりですこの部分で始まる:

(age << 8) 

を開始するには、年齢がこの形式になっています。

age   = 00000000AAAAAAA 

ここで、各Aは0または1にすることができます。

<< 8ビットを左側に8桁移動し、ゼロでギャップを埋めます。だから、あなたが得る:

(age << 8) = AAAAAAA00000000 

同様に:

gender  = 00000000000000G 
(gender << 7) = 0000000G0000000 
height  = 00000000HHHHHHH 

今、私たちは一つの変数にこれらを結合したいです。 |演算子は、各ビットを調べ、いずれかの入力でビットが1の場合は1を返します。したがって:

0011 | 0101 = 0111 

ビットが1つの入力で0の場合、もう一方の入力からビットが得られます。 (age << 8)(gender << 7)heightを見ると、これらのビットの1つが1の場合、他のビットは0になります。したがって:

packed_info = (age << 8) | (gender << 7) | height = AAAAAAAGHHHHHHH 

ここで、ビットをアンパックします。高さから始めましょう。我々は最後の7ビットを取得し、これを行うには最初の8を無視したい、我々は、入力ビットの両方が1だから、ある場合にのみ1を返し&演算子を使う:だから

0011 & 0101 = 0001 

packed_info   = AAAAAAAGHHHHHHH 
0x7F     = 000000001111111 
(packed_info & 0x7F) = 00000000HHHHHHH = height 

年齢を取得するには、8つの場所をすべて右に押して、0000000AAAAAAAAのままにします。だからage = (packed_info >> 8)

最後に、性別を取得するために、7つの場所すべてを右に押して高さを取り除きます。

packed_info   = AAAAAAAGHHHHHHH 
(packed_info >> 7)  = 0000000AAAAAAAG 
1      = 000000000000001 
(packed_info >> 7) & 1 = 00000000000000G 
+0

答えをありがとう.. – maxpayne

+5

これはスーパー良い書き込みです。私が読んだことのうち、これが何が起こっているのかを明確にした最初のことです。 – DrHall

+0

大きな説明。 – ZZeyaNN

9

これは、ビット操作ではなく、長いレッスンもよいが、最初に私があまりにもbit masking article on Wikipediaあなたを指してみましょうことができます。

packed_info = (age << 8) | (gender << 7) | height; 

年齢を取って8ビット以上の値を移動し、性別を取り、7ビットを超えて移動させ、最後のビットを占有します。

age = 0b101 
gender = 0b1 
height = 0b1100 
packed_info = 0b10100000000 
      | 0b00010000000 
      | 0b00000001100 
/* which is */ 
packed_info = 0b10110001100 

開梱は逆を行いますが、フィールド内の他の値をトリミングする(0B 01111111です)0x7Fのようなマスクを使用しています。

gender = (packed_info >> 7) & 1; 

ように働くだろう... 1への論理積何が同じであることを

gender = 0b1011 /* shifted 7 here but still has age on the other side */ 
     & 0b0001 
/* which is */ 
gender = 0b1 

注意は0で、そのビットとの論理積を「維持すること」「無視」というビットと同じです。

1

maskに存在しない(つまり値0の)ビットをxから削除する操作として、式x & maskが表示されます。つまり、packed_info & 0x7Fは、packed_infoから7番目のビットを超えるすべてのビットを削除します。

例:packed_infoはバイナリで1110010100101010であれば、その後、packed_info & 0x7fheightに、我々はpacked_infoの下位7ビットを取得し、そう

1110010100101010 
0000000001111111 
---------------- 
0000000000101010 

になります。

次に、packed_info全体を7ずつシフトしています。このようにして、すでに読んだ情報を削除します。だから私たちは前の例からの値のため111001010性別は次のビットに格納されているので、同じトリックで:& 1私たちは情報からそのビットだけを抽出しています。残りの情報は時に含まれているバックパッキング8

をオフセットすぎ、複雑ではありません:あなたはageを取る、8ビット(あなたが11100101から1110010100000000を取得)、それをシフトする、あなたが00000000を得るように、(7によってgenderをシフト)、高さを取る(下位7ビットに収まると仮定して)。その後、あなたは一緒にそれらのすべてを構成している。

AAAAAAAGHHHHHHH 

てみましょう:コメントが言う

1110010100000000 
0000000000000000 
0000000000101010 
---------------- 
1110010100101010 
2

左シフト演算子は、「2回、これを何回も乗算する」ことを意味します。バイナリでは、数に2を掛けることは、右側にゼロを加えることと同じです。

右シフト演算子は、左シフト演算子の逆です。

パイプオペレータが「又は」、互いの上にオーバーレイ2つの2進数を意味し、1は、いずれかの数である場合、その列の結果だが抽出させ、そこで1

ありますpacked_infoの操作:

// Create age, shifted left 8 times: 
//  AAAAAAA00000000 
age_shifted = age << 8; 

// Create gender, shifted left 7 times: 
//  0000000G0000000 
gender_shifted = gender << 7; 

// "Or" them all together: 
//  AAAAAAA00000000 
//  0000000G0000000 
//  00000000HHHHHHH 
//  --------------- 
//  AAAAAAAGHHHHHHH 
packed_info = age_shifted | gender_shifted | height; 

アンパックはその逆です。

// Grab the lowest 7 bits: 
//  AAAAAAAGHHHHHHH & 
//  000000001111111 = 
//  00000000HHHHHHH 
height = packed_info & 0x7F; 

// right shift the 'height' bits into the bit bucket, and grab the lowest 1 bit: 
//  AAAAAAAGHHHHHHH 
// >> 7 
//  0000000AAAAAAAG & 
//  000000000000001 = 
//  00000000000000G 
gender = (packed_info >> 7) & 1; 

// right shift the 'height' and 'gender' bits into the bit bucket, and grab the result: 
//  AAAAAAAGHHHHHHH 
// >> 8 
//  00000000AAAAAAA 
age = (packed_info >> 8); 
4

あなたは数として日付を格納するつもりだった場合は、多分あなたは100で、月、10000年掛け、一日を追加して、それを達成するでしょう。このよう7月、2、2011年のように日付が番号20110702としてエンコードされます:

year * 10000 + month * 100 + day -> yyyymmdd 
    2011 * 10000 + 7 * 100 + 2 -> 20110702 

我々はYYYYMMDDマスクで日付をエンコードされたと言うことができます。私たちは、月年に

  • シフトとして2つの左側に位置しているよう
  • は一日残しを左に4位、
  • シフトをこの操作を記述することができます。
  • 次に、3つの値を結合します。

これは、年齢、性別、身長のエンコーディングと同じことですが、著者がバイナリで考えているのと同じことです。

範囲はこれらの値を有することができることを参照してください:我々はバイナリにそれらの値を変換する場合は、我々はこのだろう

age: 0 to 127 years 
    gender: M or F 
    height: 0 to 127 inches 

を:このことを念頭に

age: 0 to 1111111b (7 binary digits, or bits) 
    gender: 0 or 1 (1 bit) 
    height: 0 to 1111111b (7 bits also) 

を、私たちは年齢をエンコードすることができますaaaaaaaghhhhhhhのマスクを使用した-gender-heightデータは、ここではバイナリの数字であり、の小数点以下はではありません。

であるように、8 ビット左に

  • シフト性別7 ビット左へと
    • シフト年齢は、高さを残します。
    • 次に、3つの値をまとめて組み合わせます。バイナリで

    、左シフト演算子(< <)は左に値N位置を移動させます。 "Or"演算子(多くの言語では "|")は値を結合します。したがって:

    (age << 8) | (gender << 7) | height 
    

    これらの値をどのようにデコードするのですか?

    これは10進数でよりバイナリで簡単です:

    • あなたは、高さを「離れてマスク」
    • シフト性別右に7ビットともそれを離れマスク、そして最終的に
    • シフト年齢は8ビットです。

    シフト右演算子(>>)は、値nの位置を右に移動します(右端のシフトされた数字が失われても)。 "And"バイナリ演算子(多くの言語で "&")はビットをマスクします。そのためには、保持するビットと破棄するビット(1ビットが保持される)を示すマスクが必要です。したがって:

    height = value & 1111111b (preserve the 7 rightmost bits) 
        gender = (value >> 1) & 1 (preserve just one bit) 
        age = (value >> 8) 
    

    1111111bはほとんどの言語で0x7fなので、そのマジックナンバーが原因です。 127(これは10進数で1111111b)を使用することで同じ効果が得られます。

  • +1

    詳細情報をありがとうございました。本当に便利でした。 – maxpayne

    2

    よりコンデンス答え:

    AAAAAAA G HHHHHHH

    パッキング:MySQLのSUM集約関数で使用された場合、すなわち場合

    packed = age << 8 | gender << 7 | height 
    

    はまたあなただけのコンポーネントを合計することができ

    packed = age << 8 + gender << 7 + height 
    

    開梱:

    age = packed >> 8 // no mask required 
    gender = packed >> 7 & ((1 << 1) - 1) // applying mask (for gender it is just 1) 
    height = packed & ((1 << 7) - 1) // applying mask 
    


    別の(長い)例:

    しかし、それは架空のIPアドレスなど で、あなたがパックしたいIPアドレスを持っていると言います132.513.151.319。実際のIPアドレスとは違って8ビット以上を必要とする256より大きなコンポーネントもあることに注意してください。

    まず、最大値を格納するためにどのオフセットを使用する必要があるかを調べる必要があります。 私たちの架空のIPでは、999より大きいコンポーネントはありません。これは、コンポーネントごとに10ビットのストレージが必要であることを意味します(最大1014までの数字を許可します)。

    今値 (1 << 10) - 1は、我々は我々がされている10個の右端のビットを超えた左側のビットを隠すために使用したバイナリマスクである

    comp1 = (packed >> 0 * 10) & ((1 << 10) - 1) // 132 
    comp2 = (packed >> 1 * 10) & ((1 << 10) - 1) // 513 
    comp3 = (packed >> 2 * 10) & ((1 << 10) - 1) // 151 
    comp4 = (packed >> 3 * 10) & ((1 << 10) - 1) // 319 
    

    を解凍することができますdec 342682502276またはbin 100111111001001011110000000010010000100

    を与える

    packed = (comp1 << 0 * 10) | (comp1 << 1 * 10) | (comp1 << 2 * 10) | (comp1 << 3 * 10) 
    

    に興味があります。

    MySQLクエリを使用した同じ例

    SELECT 
    
    (@offset := 10) AS `No of bits required for each component`, 
    (@packed := (132 << 0 * @offset) | 
          (513 << 1 * @offset) | 
          (151 << 2 * @offset) | 
          (319 << 3 * @offset)) AS `Packed value (132.513.151.319)`, 
    
    BIN(@packed) AS `Packed value (bin)`, 
    
    (@packed >> 0 * @offset) & ((1 << @offset) - 1) `Component 1`, 
    (@packed >> 1 * @offset) & ((1 << @offset) - 1) `Component 2`, 
    (@packed >> 2 * @offset) & ((1 << @offset) - 1) `Component 3`, 
    (@packed >> 3 * @offset) & ((1 << @offset) - 1) `Component 4`; 
    
    0

    私は何度も直面しています。 Bitwise AND演算子を使用すると非常に簡単です。あなたの価値を2倍の力で修飾するだけです。複数の値を格納するには、相対数(2の累乗)を追加してSUMを取得します。このSUMは、選択した値を統合します。どうやって ?

    すべての値とビット単位のANDを実行するだけで、選択されていない値と選択されていない値はゼロになります。ここ

    の説明である:

    1)値(MAYBE、NO、YES)

    2)は、2つ(2)

    YES = 2^0 = 1 = 00000001 
    NO = 2^1 = 2 = 00000010 
    MAYBE = 2^2 = 4 = 00000100 
    

    3の電源への割り当て)Iは、YESを選択してしたがってMAYBE SUM:

    SUM = 1 + 4 = 5 
    
    SUM = 00000001 + 00000100 = 00000101 
    

    この値には、YESとMAYBEの両方が格納されます。どうやって?

    1 & 5 = 1 (non zero) 
    
    2 & 5 = 0 (zero) 
    
    4 & 5 = 4 (non zero) 
    

    したがってSUMは、より詳細な説明と実装について

    1 = 2^0 = YES 
    4 = 2^2 = MAYBE. 
    

    で構成され、私のblog

    関連する問題