プチコンmkII

プチコンmkII 初心者講座

ビット演算の実習

プログラマーからひとこと

ふたたびビット演算の話です。

「もういいよ! あんまり使う場面ないし!」と思うかもしれませんが、うん、まあ、いままであんまり使う場面ありませんでしたね。
そこで今回は、もっと役立つビット演算の使い方をいくつかおぼえてみよう、という回です。

これで意外と役に立つものなんですよ。
例によって、これさえおぼえておけば、人のプログラムを読むときにグンと読みやすさがアップしますし。

難しいようで、わかってみるとカンタンなことでもあるので、ANDやORの計算に慣れながら読んでいってみてください。


ループする数

インテリ「前回までのサンプルプログラムで、ビット演算が出てきたのはおぼえてるかな?」
神崎「たしか、A=(A+1) AND 3ってカタチだったよね。」
インテリ「そうそう。コレでどういう効果が出るかは、おぼえてる?」
神崎「この行を通るたびに変数Aの数が0から1ずつ増えるんだけど、4まで増えたときには0に戻されるんだった、かな?」
インテリ「そういうコトだね。では、このビット演算を……」
ワンパク「テメエら、リズムだけでサクサクおさらいするのもイイカゲンにしやがれ! このオレもついてこれてねェぞ!」
インテリ「ヒドいなあ。じゃあ、いまのAND 3がうまく動くしくみを見直してみることから始めるかい。」
ワンパク「復習ってのもおベンキョウくさくってあんまり好きじゃねェが……
まず、サイショに変数Aが0だったトキはどうなるんだ?
」
神崎「A=(A+1) AND 3ってことは、言いかえると1 AND 3の計算になるよね。」
インテリ「それを2進数の式にすると、こうなるね。
     01
 AND 11
   = 01

ANDを使った計算はまだ忘れてないかな?
」
ワンパク「上からメセンがニクニクしいぜ! 同じ位をクラベて、両方1なら答も1、0が片方でもあったら0になるってヤツだろ。」
インテリ「そこまでワカッてるなら一気にいこうか。変数Aに入る数字が0から3までのパターンを並べてみるよ。
それぞれ+1されるから、1 AND 3 ~ 4 AND 3の式になるね。
     01        10        11        100
 AND 11    AND 11    AND 11    AND 011
   = 01      = 10      = 11      = 000

4 AND 3のときだけ、結果がゼロになるのがわかるだろう。
」
ワンパク「ホントだな。
ギャクに1~3までは、AND 3されても、そのままナニゴトもなく変化しねェのがフシギだぜ。
」
神崎「そうか、11みたいに全部1で並んだ数でANDすると、数はなにも変わらないでそのまま出てくるんだね。」
インテリ「おっ、いいトコロに目をつけたね。そのとおりで、たとえばAND 2だと、そうウマくはいかないんだ。
     01        10        11        100
 AND 10    AND 10    AND 10    AND 010
   = 00      = 10      = 10      = 000

」
ワンパク「タシカになんだか、スッキリしねえケッカになるな。」
インテリ「これがAND 7だとどうかな? 7を2進数になおすと111で、1が並ぶ数字だね。
     001        010    …        111        1000
 AND 111    AND 111    …    AND 111    AND 0111
   = 001      = 010    …      = 111        0000

」
神崎「やっぱり3ケタまではゼッタイ変化しないで、上の数が4ケタになったとたんにゼロになるよ!」
ワンパク「……ANDと1が並ぶ数を使うと、ある数まではフツウに増えて、ある数まで増えたらゼロにリセットされるってコトなのか?」
インテリ「悪くないリカイだよ。
A=(A+1) AND 7が「8回ループするたびに1回だけゼロになる」ものと考えると、IF A==FALSE THENとすれば8回に1度だけトクベツなコトができるね。
」
神崎「かならず8回目にゼロに戻ってくるのもベンリかもね。ON A GOTOを使ったプログラムでも、自動的にループになるよ。」
ワンパク「タシカにサンプルプログラムでやったコトも同じか……。
で、111みたく1が並ぶ数ってのはナニとナニがあるんだ? 3と7はワカッたが、いちいち探すのはメンドくせェぜ!
」
インテリ「だいじょうぶ、それにはパターンがあるんだ。先にそういう数を見せた方が早いかな?
2進数にすると
1の連続になる数字
10進数2進数
11
311
7111
151111
3111111
63111111
1271111111
25511111111
::
神崎「1、3、7、15、31……うーん、パターンがあるような、ないような……」
ワンパク「ナンだか数字パズルみたいになってきたな。ニガテなテンカイだぜ!」
インテリ「ゼロから数え始めるコンピューターのクセに合わせて、数を1ずつ増やすとどうかな?」
神崎「ん? 2、4、8、16、32……あっ! ぜんぶ倍になってるよ!」
インテリ「2の倍は4、4の倍は8、8の倍は16……いわゆる「2のべき乗」だね。2のべき乗マイナス1と考えれば、アンガイおぼえやすいだろう。「64」や「256」なんかはゲームでも見たことある数じゃないかな?」
ハカセ「64分の1の確率でアイテムをドロップ、とかアリガチじゃな。
コンピューターとアイショウのいい数じゃからのう。ムカシはバキ●ラを256回撃つと倒せるというウワサが……
」
ワンパク「ケッ、ムカシ話にはキョウミがねえぜ!」
ハカセ「ま、まあそれもこれもビットの考えカタにそっとるのじゃよ。」
インテリ「この数だと「63」にWAIT命令を組みあわせると、こんな風にもできるね。
0001#. @LOOP
0002#. WAIT(1)
0003#. A=(A+1) AND 63
0004#. PRINT A
0005#. GOTO @LOOP
」
ワンパク「WAIT(1)は60分の1秒だけ待つってコトだろ。それとコレとどうカンケイが……」
神崎「ビット演算では64回に1回リセットだから……1秒と4/60秒だけループした時に、変数Aがゼロにリセットされることになるね。」
インテリ「つまり「だいたい1秒」でリセットされるカウンターだね。ここではただのPRINTだけど、ゲームのタイマーや1秒ごとにたまるダメージとか、使いどころはあるんじゃないかな。」
ワンパク「オイオイ、「だいたい1秒」って、そんなテキトウでいいのか!?」
ハカセ「目に見えるワケではないからのう、体感時間としてはアリじゃ。ぶっちゃけプログラムにはよくある手じゃよ。
その後、プログラムを解析されて「なんちゃって1秒」だとバレるところまでふくめてワンセットじゃ。
」
ワンパク「は、はかりしれねえセカイだぜェ……。」

BUTTON()命令の種明かし

ワンパク「ン? さっきの「2、4、8、16、32……」って数、マエにも見た気がするな……」
神崎「そういえば、BUTTON()命令に使われてる数字がそうだったよ!
十字ボタン↑1Aボタン16Lボタン256
十字ボタン↓2Bボタン32Rボタン512
十字ボタン←4Xボタン64START1024
十字ボタン→8Yボタン128SELECT2048
」
インテリ「ソレもビット演算の考え方を使ってるんだけど、しくみは飛ばしておぼえていたね。ためしに2進数に直してみるよ。
10進数2進数
11
210
4100
81000
1610000
32100000
::
」
ワンパク「そうか、2進数にするとキリがいい数だったんだな。ソコまではワカったが……コレとビット演算がカンケイあんのか?」
インテリ「これだけだと、まだわかりづらいね。2進数のケタをそろえてみよう。
10進数2進数
1000001
2000010
4000100
8001000
16010000
32100000
::
先頭にゼロをつけただけで、数が変わったわけじゃないよ。
」
神崎「ははあ、右からジュンバンに1ケタずつ、ボタンと1のペアができてるんだね。こうやって見ると数字っていうよりは、記号みたいだよ。」
インテリ「タシカに。それぞれのケタがスイッチで、0が「オフ」で1が「オン」だって考え方があるんだ。
だから、こういう表でも1になっているケタを「ビットが立っている」と言ったりするね。
」
神崎「0から1になるのが、「ビットが立つ」ってことか。」
インテリ「000010なら第1ビットが立っている、なんて言うね。1ケタ目が第0ビット、2ケタ目なら第1ビットさ。」
ワンパク「またゼロから始める数えカタかよ……ギョウカイ用語はいいから、サッサとハナシをススメやがれ!」
インテリ「じゃあ、たとえば「十字ボタン↑」と「Aボタン」が同時押しされた時で考えてみよう。
10進数なら1+16で17が変数に入るけど、この数を2進数にするとどう見えるかな?
」
神崎「えーと、こうなるかな。
10進数2進数
1000001
16010000
1+16=17010001
」
ワンパク「お、オォ? そのまま組み合わさったみてェな数になったぜ!」
インテリ「こういう「1、2、4、8、……」と倍々になっている数のトクチョウだね。数をたすとビットが立ったままキレイに合わさるんだ。」
神崎「ますます記号みたいだね。
つまりBUTTON()命令だと、第0ビットが「十字ボタン↑」のスイッチになっていて、第4ビットは「Aボタン」のスイッチってことだね。
十字ボタン↑1000000000001
十字ボタン↓2000000000010
十字ボタン←4000000000100
十字ボタン→8000000001000
Aボタン16000000010000
Bボタン32000000100000
Xボタン64000001000000
Yボタン128000010000000
Lボタン256000100000000
Rボタン512001000000000
START1024010000000000
SELECT2048100000000000
」
ワンパク「ナカナカおもしれえハナシだが……だからナンのイミがあるんだって気もするぜ!」
インテリ「ここからがベンリなところだよ。
BUTTON()命令で言えば、Aボタンが押されているかをチェックしたい時はどうするんだったかな?
」
ワンパク「何もカンガエずに、IF文でAND 16と書けばイイって、教わったな。
0000#. @LOOP
0001#. B=BUTTON()
0002#. IF B AND 16 THEN PRINT”Аカ゛オサレテマス”
0003#. GOTO @LOOP
」
インテリ「じゃあ、今度はちゃんと見てみよう。
いま出てきた「十字ボタン↑」と「Aボタン」が同時押しされた時の数「010001」に、AND 16のビット演算をしてみるね。
     010001
 AND 010000
   = 010000

」
神崎「あっ! Aボタンのスイッチになってる第4ビットだけが立ってるよ!」
インテリ「わかってきたかな。チェックしたいケタに1を、それ以外のケタに0を入れてANDで演算すれば、そのビットが立っているかどうかスグにわかるんだ。
このトクチョウをうまく使ったのが、BUTTON()命令とANDを使ったIF文だね。
」
ワンパク「ゴクリ……てことは、第5ビットのBボタンが押されてるかどうかで考えると……
     010001
 AND 100000
   = 000000

ヤッパリ答はゼロ、第5ビットは立ってねえってコトがワカるぜ!
」
インテリ「「1、2、4、8、……」という数で組み立てておくと、こういうチェックにベンリなのがわかるね。
頭で考えるよりも、こういう数のパターンをひとつおぼえておくといいよ!
」

ビット演算を使ったフラグ

ワンパク「フーム……このパターンをBUTTON()命令イガイに使うとすると、トウゼン変数だよな。フラグなんかにベンリそうな気はするぜ!
どうするのがイイのかはサッパリ思いつかねェけど。
」
インテリ「スッキリあきらめたね。
まあ、さっきのBUTTON()命令だって、「上ボタンを押すと第0ビットのフラグが立つ」と考えればリッパにフラグさ。その応用で作ればいいよ。
」
ワンパク「ナルホド、そういうミカタもあるか……。」
神崎「さすがにBUTTON()なみに11ケタいるとは思えないけど、4ケタあれば、4つのオン・オフをいちどに見られるフラグになるワケだね。」
ワンパク「イヤちょっと待てよ! BUTTON()ならボタンを押せばカッテにフラグが立ってくれたがよォ、自分のプログラムの中でフラグを立てたり消したりするのは、どうやりゃいいんだ?」
神崎「たしかに、10進数みたくA=A+1みたいに書くわけにはいかないよね。第2ビットを1にするには、ええと……」
インテリ「フラグを立てるならビット演算のORを使うのがキホンかな。
たとえばまっさらな変数Aがあったとしよう。A=0だから、2進数でも0000なのはわかるよね。
この3ケタ目(第2ビット)を立てるには、こうするよ。
    0000
 OR 0100
  = 0100

ここでORに使った0100は、10進数で言いかえれば4。
つまり、A=A OR 4と書けば、第2ビットが立つことになるね。
」
ワンパク「ORを使うのはワカッたが、イチイチ10進数に直すのがカッタルイぜ!」
ハカセ「なれないウチは並んだ2進数の右ハジから、「1、2、4、8、……」と倍にしながら数えていくのがカンタンかもしれんな。10進数で右ハジから「一、十、百、千、万、……」とケタを数えるノリでいけるぞい。」
神崎「第2ビットが立っているときに、もういちどOR 4すると……
    0100
 OR 0100
  = 0100

やっぱりそのままだね。ORはビットを立てるために使うってコトか。
」
インテリ「オン・オフを逆にしたい時は、XORがオススメだね。
ためしに1100と1000、どっちにも0100でXORしてみるよ。
     1100         1000
 XOR 0100     XOR 0100
   = 1000       = 1100

1でXORした第2ビットだけがひっくり返って、ホカのケタはそのまま残るのがわかるだろう。
」
神崎「XORって使い方がムズカシいと思ってたけど、こういう時にベンリなんだね。」
ワンパク「じゃあ、とにかく0にしたいってトキは……そうか、ANDだな?
     0100         1111
 AND 1011     AND 1011
   = 0000       = 1011

1011でAND、10進数で言えばAND 11だな。
これでホカのケタはそのままで、第2ビットだけが0になったぜ!
」
ハカセ「なかなかワカッてきたではないか。
さっきの復習じゃが、ANDを使ったフラグチェックには、こういうテクニックもあるのう。
     1110
 AND 0100
   = 0100

AND 4すると、第2ビット以外はゼロになったじゃろう。
」
ワンパク「BUTTON()で使ったテクだな! 第2ビット以外をゼロにすれば、第2ビットのオン・オフだけがハッキリする。ツマリ……エー……」
神崎「そうか! そうなると、IF A THENの形が使えるんだね。」
インテリ「そのトオリ。1行にまとめると、IF A AND 4 THEN~だね。
もちろんホカにもA=A AND 4:IF A==FALSE THEN~ なんかでもいいけど、とにかく決まったケタのナカミだけがスグにわかるよね。
」
ワンパク「ホカのケタがまるまるゼロになっちまうが……、ああ、そこは変数のナカミをあらかじめコピーしとけばいいのか?」
ハカセ「そうじゃな、A=Bとか逃がしてから、B=B AND 4などとするのがアンゼンじゃろうの。」
インテリ「まだまだイロイロなテクニックはあるよ。なかなかオクの深い世界だろう。」
ワンパク「とっつきはワルいが、使いミチはワカッてきた気がするぜ。てコトは、このアトすぐに使う場面があるワケだな?」
ハカセ「いや、そうともかぎらんが。」
神崎「えー……?」

今回のまとめ

ループする数
A=(A+1) AND 3
こう書くと、A=0から始めて、この式を3+1回くり返したとき、変数Aは0に戻ります。
3以外に、7、15、31、63……と、「2のべき乗」マイナス1の数で同じパターンが使えます。
ビット演算を使ったフラグ
たとえば2進数の0000(10進数の0)を「4つ並んだフラグ」と見なせば、4種類のオン・オフを管理できます。
0001(10進数の1)なら「第0ビットが立った」状態、0100(10進数の4)なら「第2ビットが立った」状態と言えます。
他のケタを変化させずに第2ビットだけを1にしたければOR 4(2進数の0100)、第2ビットだけを0にしたければAND 11(2進数の1011)することになるでしょう。
第2ビットの状態だけを反転させるには、XOR 4(2進数の0100)がいいでしょう。
わざとAND 4(2進数の0100)することで、第2ビット以外を0にして、第2ビットの状態をハッキリさせる方法もあります。
こういったあるひとケタだけが1になっている数は、10進数に直せば「1、2、4、8……」と倍々に増えていく数になります。