2019年8月8日木曜日

ビット演算メモ(ANDでのマスク等)

 情報記録/共有として、ビット演算について記述しまとめて置こうと思います。


ビット演算するために必要な知識:


  • 2進法、10進法、16進法
  • 変数、フラグ、用途、ビット
  • 論理演算(AND、OR、NOT)
  • マスク的思考



利用するシーン:


 何かのソフト開発をするときに、最小限の情報量で最大限のフラグ組み合わせを保持・保存したくなる場合が多々あります。
 例えば、「複合的なエラーコード一本化」「表示状態の複合的保持」「設定状態の複合的チェック項目」等を、“一つの情報”として変数や文字列、情報保存する際に有用です。
 それぞれ無数に保存するよりかは、一つにまとめて保存、それでいて混ざってしまわない保存を前提として有効です。


例:


 例えば、2進法で4×4のビットを構成して考えていきたいと思います。

 0000 0000 (16進数では「0x00」、10進数では「0」)
 ~
 1111 1111 (16進数では「0xFF」、10進数では「255」)

 この範囲での組み合わせは、最大8種、256通りの組み合わせが可能です。

 それぞれの桁に意味を定義し、その8種それぞれがON/OFFしつつ、同時に違う桁の情報がONになることも想定した管理・保存する事が可能になります。


8ビットの2進法数値カンペ:


 2進数の各桁が「1」だった時の10進数、16進数の値を書いておきます。
 (下の桁(右)から、1、2、4、と倍々になっていくだけですが)

 128 64 32 16  8 4 2 1

 0000 1010 → 0+0+0+0 + 8+0+2+0 = 10 → 0x0A
 0000 1110 → 0+0+0+0 + 8+4+2+0 = 14 → 0x0E
 0010 1010 → 0+0+32+0 + 8+0+2+0 = 42 → 0x2A
 1010 1010 → 128+0+32+0 + 8+0+2+0 = 170 → 0xAA
 1111 1111 → 128+64+32+16 + 8+4+2+1 = 255 → 0xFF


 (よく使うカンペなので、縦にも書いておきます)

 ビット桁
 桁単体の値
 合計最大値
 0~全個数
1 1 1 2
2 2 3 4
3 4 7 8
4 8 15 16
   
5 16 31 32
6 32 63 64
7 64 127 128
8 128 255 256
   
9 256 511 512
10 512 1023 1024
11 1024 2047 2048
12 2048 4095 4096
   
13 4096 8191 8192
14 8192 16383 16384
15 16384 32767 32768
16 32768 65535 65536
   


使用例:


 ここで仮にルールを定義します。

 適当な作成のソフトウェア内の設定項目で、「F2モード:ON」(←フラグ2番目をON)にした場合は2ビット目(2進数の右から2桁目)を「1(true)」と上げる。OFFの場合は「0(false)」に落とす。
 同様に、F3は3桁目、F4は4桁目のフラグを立てたり落としたりするとします。
 (他のフラグ(ビット桁)は予約として、何が入っていても無視、将来使う予定とする)

 F2だけがON
  ????001?

 F3だけがON
  ????010?

 F4だけがON
  ????100?

 F3とF4がON
  ????110?

 F2~4がON
  ????111?

 組み合わせはほかにもありますが、上記の様な例が考えられます。
 これを仮に「bitTemp」に代入されていたとします。

 あとは条件として、「&」を使い、マッチするかどうかを判断させまます。

 例えば、F2が1(フラグが立っている)かどうか(下2桁目に1が立っているかどうか)
 if( ( bitTemp & 0x02 ) != 0 ) { ~~~~~ }

 F2とF3のどちらかが1かどうか(下2桁目と3桁目=2+4=6→0x06)
 if( ( bitTemp & 0x06 ) != 0 ) { ~~~~~ }

 F2とF4のどちらかが1かどうか(下2桁目と3桁目=2+8=10→0x0A)
 if( ( bitTemp & 0x0A ) != 0 ) { ~~~~~ }

 F2とF4のどちら1かどうか(下2桁目と3桁目=2+8=10→0x0A)
 if( ( bitTemp & 0x0A ) =0x0A ) { ~~~~~ }

 ※C言語などでは!=0等の判定は必要なく、ifの結果がtrueかどうかで判断できると思いますので、カッコ1重のif( bitTemp & 0x0A ) {でよいと思います。
 ※実際には試していません。考え方としてのメモですので、必要に応じて適時使用してください。

 ここで、なぜ!=0や、>0、trueでいいのか?という疑問が出ます。
 答えは「論理積だから・・・」というしかないのですが、上の例を元に、細かく説明していくと以下の様になります。

 bitTemp の中身が、仮に「00111010」だったとします。

 これに対し、「0x0A」「&」でぶつけることになるので、どちらも「1」のビットのみ「1」が返ってくることになります。

 00111010
 &
 00001010 0x0A
 =↓
 00001010 0x0A


 もし、ぶつける値を「0x08」とした場合は、

 00111010
 &
 00001000 0x08
 =↓
 00001000 0x08

 となり、下2桁目の1は判断対象に含まれません。


 逆に、ぶつける値を「0x04」とした場合は、

 00111010
 &
 00000100 0x04
 =↓
 00000000 0x00

 となりますので、結果「0」(false)となります。


 最後に、あえてぶつける値を「0x0C」(12)とした場合、意図としては4ビット目のF4(8)と、3ビット目のF3(4)のどちらかが「1」として使いたかったり、両方が「1」かどうかを検出したい時が想定されます。

 00111010
 &
 00001100 0x0C
 =↓
 00001000 0x80」 ← 0は超えているのでどれかは「1」という解釈ができる。

 もし、マスクした結果のどれもがマスク元と同じであるか(すべてが「1」かどうか)を検出するのであれば、「0x0C」をマスクとした場合、結果が「0x0C」となっているかを比較することで判断できます(上記の場合は、結果が「0x80」と「0x0C」とは異なるため、偽“false”という事になります。

 よって、上にも書きましたが、

 F2とF4のどちら1かどうか(下2桁目と3桁目=2+4=6→0x0A)
 if( ( bitTemp & 0x0A ) =0x0A ) { ~~~~~ }

 という完全HITをさせる条件式を使うケースも考えられます。

 論理和や排他的論理和などは機会があれば別な時にでも書き足そうと思います。


- PDS PIS 研究室 室長 -

0 件のコメント:

コメントを投稿