LoginSignup
6
11

More than 5 years have passed since last update.

ビット演算について

Posted at

社内で先輩エンジニアから教えてもらったビット演算に関する考え方、まだしっかり
理解できていないけど、保管しておきたいので。


「フラグを検知する仕組み」と思ってください。
詳しく知りたかったらググってください。いっぱいあるので。
上記の通りフラグを検知できるので、1カラムに保管したデータに対して以下の判定をすることができます。
・「A」があるか?(無いか?)
・「B」があるか?(無いか?)
・「AとB」の両方があるか?(無いか?)
上記の機能によりnのデータを1カラムで表すことができます。
さらに、SQLのWHERE句でも上記の判定をできるので、検索等においても利用できるので大変便利。

但しビットなどといってもデータは単なる「正の整数」(int)なので、32bitで表現できる整数の最大値は「4294967295」になります。
64bitだともっと扱えますがどちらにしても制限がある事には変わりないので32bitの最大値で考えた方が無難です。
(例えばMySQLが64bitバージョンを利用してもrubyが32bitで実行されるとMySQLからデータ取得した時に桁溢れを起こします。
環境に依存するコーディングや設計は極力避けましょう)

そうなると表現できる種類は32通りになります。
よって、選択肢が(増えても)30以下ならビットで表すのも方法の1つ。

ちなみに10進数だと人が目で見て分かり辛いので、プログラム上は通常16進で表記します。
以下はよく使う簡単な例です。

●定義
※本当は先日に記載の通り定数化&配列定義して利用する。
nil            未入力
0x00000        特に無し
0x00001        総合感冒薬
0x00002        咳止め
0x00004        鼻水止め
0x00008        熱冷まし・痛み止め
0x00010        痰を出やすくする薬
0x00020        抗生剤
0x00040        インフルエンザの薬
0x00080        喘息の薬

●データ
0x00038        熱冷まし・痛み止め、痰を出やすくする薬、抗生剤

●SQLの例
例)「痰を出やすくする薬」を含むのを取得
SELECT * FROM interview_sheets WHERE (medecine_requerst & 0x00010)<>0

例)「熱冷まし・痛み止め」「痰を出やすくする薬」の両方を含むものを取得
SELECT * FROM interview_sheets WHERE (medecine_requerst & 0x00018)=0x00018

●rubyの例
例)「痰を出やすくする薬」を含むのを取得
if (medecine_requerst & 0x00010) != 0

end

例)「熱冷まし・痛み止め」「痰を出やすくする薬」を変数にセット
medecine_requerst = 0x00008|0x00010

「単純に10進数の0~15を1桁で表す(0123456789ABCDEF)方法」だけで充分です。
換算方法は不要です(必要になったら変換サイトでOK)。
16進数は先頭に「0x」を付けるというルールになっています。
(桁数は「人間の見た目」の為に0パディングしてます。なくても良いです)

16進数        10進数
0x0001         1
0x0002         2
0x0003         3
0x0004         4
0x0005         5
0x0006         6
0x0007         7
0x0008         8
0x0009         9
0x000A        10
0x000B        11
0x000C        12
0x000D        13
0x000E        14
0x000F        15
0x0010        16 ※Fまで来たので桁を繰り上げる。
0x0011        17

昨日の例の定義で、0x001、0x002、0x004、0x008と「飛んで」いるのはビット演算の為です。
0は「0」です。
1はフラグとして利用します。
2はフラグとして利用します。その前に1が登場しましたが数字1個では演算ができませんので2もフラグとして利用します。
3は、その前に登場した1と2の合計が3になるので、3をフラグとして利用すると、1+2を表しているのか、3というフラグを表しているのか区別が出来ません。よってフラグとしては利用しません。
4はフラグとして利用します。3はフラグではない(1+2を表している)ので他に演算するフラグが無い為フラグとして利用します。
、、、を繰り返すと以下のようになります。

16進数        10進数
0x0001         1        
0x0002         2        
0x0003         3        =1+2
0x0004         4        
0x0005         5        =2+3
0x0006         6        =2+4
0x0007         7        =1+2+4
0x0008         8        
0x0009         9        =1+8
0x000A        10        =2+8
0x000B        11        =1+2+8
0x000C        12        =4+8
0x000D        13        =1+4+8
0x000E        14        =2+4+8
0x000F        15        =1+2+4+8
0x0010        16        
0x0011        17        =16+1

で、上記を繰り返しながら10進数が32bitのintの最大値の4294967295になるのは、
16進数で表すと0xFFFFFFFFになります。
この時「フラグ」として利用できる数が32個になります。

16進数        10進数
0x0001         1
0x0002         2
0x0004         4
0x0008         8
0x0010         16
0x0020         32
0x0040         64
0x0080         128
0x0100         256
0x0200         512
0x0400         1024
0x0800         2048
...

1248を桁を変えながら繰り返していくので目で見て分かり易いですね。
同じ事を16進と10進で表してみます。

(0x0110 & 0x0010)<>0 → 0x0110に0x0010が含まれているか?→うん。入ってるね。
(272 & 16)<>0 → 272に16が含まれているか?→は?何言ってんの?となる。
6
11
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
11