論理演算について
・ | パソコンはデジタルで管理されている |
・ | 2進数と16進数について |
・ | 論理演算を使ってみよう |
・ | シフト演算を使ってみよう |
・ | 1つの変数を32個のフラグとして使おう |
・ | 1つの変数に複数の整数値を持たせよう |
パソコンはデジタルで管理されている
■デジタルとは?
「 0 と 1 」 の2つの記号だけで表現されたデータの事を、デジタルといいます。
パソコンはすべて2進数で管理されていて、「 0 と 1 」の集まりでいろんなデータを表現する事ができます。
■メモリの状態
例えば変数に 6 という数値を入れてみます。
人間にとっては 6 という 10 進数の数値と認識できますが、
パソコン内部では 2 進数で扱われます。
メモリの中身は下のようになります。
パソコン内部では 2 進数で扱われます。
場所 | 値 |
変数 (10進数) | 6 |
メモリ (2進数) | 00000000000000000000000000000110 |
注.整数のときです。小数の場合や文字の場合はデータの扱いが変わります。
■デジタルの単位について
「 0 と 1 」を表現できる、「桁1つ分」の単位を 1 bit(ビット)といいます。
ビットが 8 個 集まったものを 1 byte(バイト) といいます。
8 bit = 1 byte となります。
■1つの変数(整数型)のサイズ
整数を入れるための変数であれば、変数ひとつにつき 32 bit (4 byte)の領域を使用する事ができます。
これは 10 進数でいうと -2147483648 から 2147483647 までの数値を表現できます。
ActionScriptでは論理演算やシフト演算を使用したときだけ 4 byte での計算となり、通常の四則演算でこれより大きい数値を扱うことは可能です。
次は 2 進数について見ていきましょう。
2進数と16進数について
■各進数の動作を確認する
2進数と16進数と10進数で表現したときの変化を確認できます。
■2進数について
2進数は、
0 の状態から 1 ずつ足していくと 1 桁目が 0,1 と増えます。
1 に 1 を足すと 0 に戻り、次の桁に 1 が足されます。
2進数は以下のように桁が増えます。
10進数 | 2進数 |
0 | 0 |
1 | 1 |
2 | 10 |
3 | 11 |
4 | 100 |
5 | 101 |
6 | 110 |
7 | 111 |
8 | 1000 |
9 | 1001 |
10 | 1010 |
11 | 1011 |
12 | 1100 |
13 | 1101 |
14 | 1110 |
15 | 1111 |
16 | 10000 |
17 | 10001 |
18 | 10010 |
19 | 10011 |
20 | 10100 |
21 | 10101 |
22 | 10110 |
23 | 10111 |
24 | 11000 |
25 | 11001 |
26 | 11010 |
27 | 11011 |
28 | 11100 |
29 | 11101 |
30 | 11110 |
31 | 11111 |
32 | 100000 |
■16進数について
16進数は、
0 の状態から 1 ずつ足していくと 1 の位が 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F と増えていきます。
F に 1 を足すと 0 に戻り、次の桁に 1 が足されます。
16進数は以下のように桁が増えます。
10進数 | 16進数 |
0 | 0 |
1 | 1 |
2 | 2 |
3 | 3 |
4 | 4 |
5 | 5 |
6 | 6 |
7 | 7 |
8 | 8 |
9 | 9 |
10 | A |
11 | B |
12 | C |
13 | D |
14 | E |
15 | F |
16 | 10 |
17 | 11 |
18 | 12 |
19 | 13 |
20 | 14 |
21 | 15 |
22 | 16 |
23 | 17 |
24 | 18 |
25 | 19 |
26 | 1A |
27 | 1B |
28 | 1C |
29 | 1D |
30 | 1E |
31 | 1F |
32 | 20 |
■2進数と16進数は相性がよい
「2 進数」と「16 進数」は相性がよく、
「16 進数の 1 桁」を「2 進数の 4 桁」で表現できるため、下のように桁が崩れることなく表現ことができます。
「16 進数の 1 桁」を「2 進数の 4 桁」で表現できるため、下のように桁が崩れることなく表現ことができます。
2進数 | 16進数 |
0000 0000 0000 1111 | 0 0 0 F |
0000 0000 1001 0000 | 0 0 9 0 |
0001 0010 0000 0000 | 1 2 0 0 |
しかし「2 進数」と「10 進数」は相性が悪く、
「2 進数」を「10 進数」で表現すると下のように桁がずれるため、簡単に変換する事ができません。
「2 進数」を「10 進数」で表現すると下のように桁がずれるため、簡単に変換する事ができません。
2進数 | 10進数 |
0000 0000 0000 1111 | _ _ 1 5 |
0000 0000 1001 0000 | _ 1 4 4 |
0001 0010 0000 0000 | 4 6 0 8 |
■ 16 進数でプログラミングする
プログラムで、「2 進数」の数値を記述する事はできません。
16 進数の数値は記述する事ができるので、かわりに 16 進数を使用します。
「16進数」の数値を記述するには、数値の頭に 0x を付けます。
16進数で記述する
var data1 = 0x10; // 16
var data2 = 0x120b; // 4619
var data3 = 0xc82a1467; // 3358200935
なお「8 進数」の数値を記述するには、数値の頭に 0 を付けます。
■頻繁に使用する 16 進数
16 進数の 1,2,4,8 は 2 進数になおすと1つだけビットが立っている状態になります。
これは非常によく使用するパターンとなります。
2進数 | 16進数 |
0001 | 1 |
0010 | 2 |
0100 | 4 |
1000 | 8 |
2進数 | 16進数 |
0000000000000001 | 0x0001 |
0000000000000010 | 0x0002 |
0000000000000100 | 0x0004 |
0000000000001000 | 0x0008 |
0000000000010000 | 0x0010 |
0000000000100000 | 0x0020 |
0000000001000000 | 0x0040 |
0000000010000000 | 0x0080 |
0000000100000000 | 0x0100 |
0000001000000000 | 0x0200 |
0000010000000000 | 0x0400 |
0000100000000000 | 0x0800 |
0001000000000000 | 0x1000 |
0010000000000000 | 0x2000 |
0100000000000000 | 0x4000 |
1000000000000000 | 0x8000 |
■数値から文字列に変換する
数値から、2進数表記の文字列に変換したい場合は以下のように記述できます。
2進数表記の文字列に変換する
var num = 12315;
trace(num.toString(2));
数値から、8進数表記の文字列に変換したい場合は以下のように記述できます。
8進数表記の文字列に変換する
var num = 12315;
trace(num.toString(8));
数値から、16進数表記の文字列に変換したい場合は以下のように記述できます。
16進数表記の文字列に変換する
var num = 12315;
trace(num.toString(16));
論理演算を使ってみよう
■論理演算の種類
論理演算の種類の中で特に以下の3種類に注目します。
記号 | 名称 | 意味 |
| | OR演算子 | 論理和 |
& | AND演算子 | 論理積 |
~ | NOT演算子 | 論理否定 |
■ or 演算子について
'|' は「論理和」を意味します。
下の表のように2つのビットを比較して、どちらかが 1 だったら結果は 1 になります。
1番目のデータ | 論理和 | 2番目のデータ | → | 結果 |
0 | | | 0 | → | 0 |
0 | | | 1 | → | 1 |
1 | | | 0 | → | 1 |
1 | | | 1 | → | 1 |
■ and 演算子
'&' は「論理積」を意味します。
下の表のように2つのビットを比較して、どちらも 1 の場合だけ結果は 1 になります。それ以外は 0 です。
1番目のデータ | 論理積 | 2番目のデータ | → | 結果 |
0 | & | 0 | → | 0 |
0 | & | 1 | → | 0 |
1 | & | 0 | → | 0 |
1 | & | 1 | → | 1 |
■ not 演算子について
'~' は「論理否定」を意味します。
下の表のように、 0 の場合は 1 になり 1 の場合は 0 になります。
否定を使うとビットの 0 と 1 を反転させることが出来ます。
論理否定 | データ | → | 結果 |
~ | 0 | → | 1 |
~ | 1 | → | 0 |
■実際にプログラムで使ってみよう
では早速プログラム例を見ながらどのような演算が行われるか確認してみましょう。
■論理和を使ってみよう
まずは、論理和です。
変数 a と変数 b を論理和で計算し、得られた結果を変数 c に格納します。
例) or 演算を使った計算
var a = 0xC1482C0A;
var b = 0x00FF00FF;
var c = a | b;
さて、16進数の数値が出てきました。
パソコン内部ではデジタルとなるので、 16 進数を 2 進数で表記してみます。
2 進数での表記を確認する |
変数 a と変数 b の各ビットを一桁ずつ「OR 演算」で計算して、下の変数 c に出力してみます。
「OR 演算」は「どちらかが 1 だったら結果は 1 になる」ので変数 c は以下のようになります。
各ビットを 「OR 演算」する |
■論理積を使ってみよう
次は、論理積です。
変数 a と変数 b を論理積で計算し、得られた結果を変数 c に格納します。
例) and 演算を使った計算
var a = 0xC1482C0A;
var b = 0x00FF00FF;
var c = a & b;
16 進数を 2 進数で表記してみます。
2 進数での表記を確認する |
変数 a と変数 b の各ビットを一桁ずつ「AND 演算」で計算して、下の変数 c に出力してみます。
「AND 演算」は「どちらも 1 の場合だけ結果は 1 」になるので変数 c は以下のようになります。
各ビットを 「AND 演算」する |
■論理否定を使ってみよう
最後に、論理否定です。
変数 a を論理否定で計算し、得られた結果を変数 b に格納します。
例) not 演算を使った計算
var a = 0xC1482C0A;
var b = ~a;
16 進数を 2 進数で表記してみます。
2 進数での表記を確認する |
変数 a の各ビットを一桁ずつ「NOT 演算」で計算して、下の変数 b に出力してみます。
「NOT 演算」は「ビットが反転」するので変数 b は以下のようになります。
各ビットを 「NOT 演算」する |
シフト演算を使ってみよう
■シフト演算とは?
シフト演算を使用すると、
2 進数のビット列を左か右にスライドさせる事ができます。
■シフト演算の種類
シフト演算の以下の3種類に注目します。
記号 | 種類 | 意味 |
<< | 論理シフト | 全体を左に移動、一番右には 0 を補充 |
>>> | 論理シフト | 全体を右に移動、一番左には 0 を補充 |
>> | 算術シフト | 全体を右に移動、一番左には元々存在した値と同じものを補充 |
■左シフト演算を使ってみよう
まず、左シフト演算です。
変数 a を左シフト演算で計算し、得られた結果を変数 b に格納します。
例) 左シフトを使った計算
var a = 0x60F842AB;
var b = a << 2;
16 進数を 2 進数で表記してみます。
2 進数での表記を確認する |
「 a << 2 」 で左に 2 回スライドさせるという意味があります。
まず 1 回スライドさせます。
1 回左に論理シフトする ( 1 回目) |
一番左(最上位ビット)の値は消滅します。
一番右(最下位ビット)には 0 が補充されます。
もう一度 1 回スライドさせます。
1 回左に論理シフトする ( 2 回目) |
一番左(最上位ビット)の値は消滅します。
一番右(最下位ビット)には 0 が補充されます。
このことから左シフト演算は以下のような動作をすることがわかります。
■左シフト演算のまとめ
左に移動することにより…
・一番左 (最上位ビット) の溢れた値は消滅します。
・一番右 (最下位ビット) には 0 が補充されます。
・一番右 (最下位ビット) には 0 が補充されます。
■右シフト演算の種類
次に、右シフト演算です。
右シフト演算には、「論理シフト」と「算術シフト」の2種類があります。
まずは、右論理シフトについて見てみましょう。
■右論理シフトを使ってみよう
変数 a を右論理シフトで計算し、得られた結果を変数 b に格納します。
例) 右論理シフトを使った計算
var a = 0x60F842AB;
var b = a >>> 2;
16 進数を 2 進数で表記してみます。
2 進数での表記を確認する |
「 a >>> 2 」 で右に 2 回、論理的にスライドさせるという意味があります。
まず 1 回スライドさせます。
1 回右に論理シフトする ( 1 回目) |
一番右(最下位ビット)の値は消滅します。
一番左(最上位ビット)には 0 が補充されます。
もう一度 1 回スライドさせます。
1 回右に論理シフトする ( 2 回目) |
一番右(最下位ビット)の値は消滅します。
一番左(最上位ビット)には 0 が補充されます。
このことから右論理シフト演算は以下のような動作をすることがわかります。
■右論理シフト演算のまとめ
右に移動することにより…
・一番右 (最下位ビット) の溢れた値は消滅します。
・一番右 (最上位ビット) には 0 が補充されます。
・一番右 (最上位ビット) には 0 が補充されます。
■右算術シフトを使ってみよう
次は、右算術シフトについて見てみましょう。
変数 a を右算術シフトで計算し、得られた結果を変数 b に格納します。
例) 右算術シフトを使った計算
var a = 0x60F842AB;
var b = a >> 2;
16 進数を 2 進数で表記してみます。
2 進数での表記を確認する |
「 a >> 2 」 で右に 2 回、算術的にスライドさせるという意味があります。
まず 1 回スライドさせます。
1 回右に算術シフトする ( 1 回目) |
一番右(最下位ビット)の値は消滅します。
一番左(最上位ビット)には 0 が補充されます。
もう一度 1 回スライドさせます。
1 回右に算術シフトする ( 2 回目) |
一番右(最下位ビット)の値は消滅します。
一番左(最上位ビット)には 0 が補充されます。
この時点では、論理シフトと算術シフトに違いがみられません。
もうひとつ別の例をみてみましょう。
例) 右算術シフトを使った計算
var a = 0x80F842AB;
var b = a >> 2;
16 進数を 2 進数で表記してみます。
2 進数での表記を確認する |
「 a >> 2 」 で右に 2 回、算術的にスライドさせるという意味があります。
まず 1 回スライドさせます。
1 回右に算術シフトする ( 1 回目) |
一番右(最下位ビット)の値は消滅します。
一番左(最上位ビット)には 1 が補充されます。
もう一度 1 回スライドさせます。
1 回右に算術シフトする ( 2 回目) |
一番右(最下位ビット)の値は消滅します。
一番左(最上位ビット)には 1 が補充されます。
2つの例を比較してみると、一番左(最上位ビット)に補充される値が変わっています。
シフト演算を行う前の一番左 (最上位) のビットに注目してみると、最初の例では 0 、次の例では 1 となっています。
このビットと同じ値が補充されています。
このことから右算術シフト演算は以下のような動作をすることがわかります。
■右算術シフト演算のまとめ
右に移動することにより…
・一番右 (最下位ビット) の溢れた値は消滅します。
・一番左 (最上位ビット) には、シフトする前の一番左のビットと同じ数値が補充されます。
・一番左 (最上位ビット) には、シフトする前の一番左のビットと同じ数値が補充されます。
■シフト演算の特性
シフト演算を一度行うと 2 進数の桁が変わります。この事から…
左に 1 回論理シフトを行うと、値が 2 倍になります。
右に 1 回算術シフトを行うと、値が 1 / 2 倍になります。
■ActionScript1.0 での論理演算やシフト演算の計算時の注意点
ActionScript 1.0 では、変数に整数値を代入すると内部で「整数型」として扱われます。
さらに、論理演算やシフト演算を使用したときだけ、一時的に「符号あり 32 bit整数型」として計算されます。
以下の例からも確認できます。
例) 論理演算を使用すると「符号あり 32 bit整数型」として計算される
var a = 4294967295;
var b = 4294967295 | 0;
trace(a); // 4294967295
trace(b); // -1
■符号なし整数型の変数について
8 bit の変数があるとします。
このビット数を使って表現できるパターンを 10 進数であらわすと 256 段階を表現できます。
正の値では 、以下のように0 から 255 までを表現できる事になります。
2進数 | 16進数 | 10進数 |
00000000 | 0x00 | 0 |
00000001 | 0x01 | 1 |
00000010 | 0x02 | 2 |
00000011 | 0x03 | 3 |
00000100 | 0x04 | 4 |
00000101 | 0x05 | 5 |
00000110 | 0x06 | 6 |
00000111 | 0x07 | 7 |
00001000 | 0x08 | 8 |
00001001 | 0x09 | 9 |
~ 略 ~ | ||
11110110 | 0xF6 | 246 |
11110111 | 0xF7 | 247 |
11111000 | 0xF8 | 248 |
11111001 | 0xF9 | 249 |
11111010 | 0xFA | 250 |
11111011 | 0xFB | 251 |
11111100 | 0xFC | 252 |
11111101 | 0xFD | 253 |
11111110 | 0xFE | 254 |
11111111 | 0xFF | 255 |
■符号あり整数型の変数について
8 bit の変数があるとします。
このビット数を使って表現できるパターンを 10 進数であらわすと 256 段階を表現できます。
この 256 段階を使ってプラスとマイナスを扱いたい場合、
半分の 128 段階をマイナス値、残り半分の 128 段階をプラス値として振り分けられ -128 から 127 までの値をを表現できる事になります。
半分の 128 段階をマイナス値、残り半分の 128 段階をプラス値として振り分けられ -128 から 127 までの値をを表現できる事になります。
0xFF の状態で 1 を加算すると、一周して 0x00
0x00 の状態で 1 を減算すれば、一周して 0xFF
…となる特性も利用され以下のような状態となります。
0x00 の状態で 1 を減算すれば、一周して 0xFF
2進数 | 16進数 | 10進数 |
10000000 | 0x80 | -128 |
10000001 | 0x81 | -127 |
10000010 | 0x82 | -126 |
10000011 | 0x83 | -125 |
10000100 | 0x84 | -124 |
~ 略 ~ | ||
11110110 | 0xF6 | -10 |
11110111 | 0xF7 | -9 |
11111000 | 0xF8 | -8 |
11111001 | 0xF9 | -7 |
11111010 | 0xFA | -6 |
11111011 | 0xFB | -5 |
11111100 | 0xFC | -4 |
11111101 | 0xFD | -3 |
11111110 | 0xFE | -2 |
11111111 | 0xFF | -1 |
00000000 | 0x00 | 0 |
00000001 | 0x01 | 1 |
00000010 | 0x02 | 2 |
00000011 | 0x03 | 3 |
00000100 | 0x04 | 4 |
00000101 | 0x05 | 5 |
00000110 | 0x06 | 6 |
00000111 | 0x07 | 7 |
00001000 | 0x08 | 8 |
00001001 | 0x09 | 9 |
~ 略 ~ | ||
01111011 | 0x7B | 123 |
01111100 | 0x7C | 124 |
01111101 | 0x7D | 125 |
01111110 | 0x7E | 126 |
01111111 | 0x7F | 127 |
■2の補数について
符号あり整数型の変数に格納された値に注目します。
一番左(最上位)のビットが 1 である場合マイナスとなっている事がわかります。
このマイナスの表現方法を2の補数といいます。
右算術シフトを使うと、一番左(最上位)のビットと同じ値が補充されるため符号が維持されます。
「2の補数」状態であるビット列から本来の 10 進数の値に変換したい場合は以下の通りに計算します。
A. 1 を減算します。
B.ビットを反転させます。
C. 2 進数から 10 進数に変換してからマイナス符号を付けます。
10 進数の値から「2の補数」に変換したい場合は以下の通りに計算します。
A.マイナス符号を外して 2 進数に変換します。
B. 1 を減算します。
C.ビットを反転させます。
1つの変数を32個のフラグとして使おう
■1つの変数を32個のフラグとして使う
ここからゲームで論理演算を扱う解説になります。
true か false の 2 パターンを管理したいときがあります。
そんな変数が、 32 個ほど必要だとします。
「Boolean 型の変数」を 32 個用意するのも 1 つの手です。
この場合、Boolean 型の変数が 1 Byte 消費するとして(実際は不明ですが)、 32 個用意する必要があるので少なくとも 32 Byte は消費する事になります。
論理演算を使えば、32 bit 変数の各ビットをスイッチと見立てて 32 個のフラグとして使用する事ができます。
この場合、 32 bit の変数を 1 個使用するので、4 Byte で済みます。
32bit変数の各ビットをスイッチのように扱う
0 0 1 0 0 1 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 1 1
■各ビットが立っている変数を用意する
各ビットが 1 つだけ立っている変数を用意します。
定数を使いたいところですが、ActionScript1.0 では定数を定義できないので変わりに変数を使用します。
お好みで名前をつけますが、「この変数は論理演算用に使用する定数である」という事をわかりやすくするために「すべて大文字にする」といった事を決めておくといいでしょう。
各ビットが立っている論理演算用の変数を定義する
var PLAYER_STAND_FLG = 0x00000001; // 立っているか
var PLAYER_DEATH_FLG = 0x00000002; // 死んでいるか
var PLAYER_AIR_FLG = 0x00000004; // 空中か
var PLAYER_COLOR_FLG = 0x00000008; // カラー使用中か
var PLAYER_ALPHA_FLG = 0x00000010; // 半透明使用中か
var PLAYER_SCALE_FLG = 0x00000020; // 拡大中か
var PLAYER_MOVE_FLG = 0x00000040; // 移動中か
var PLAYER_ROT_FLG = 0x00000080; // 回転中か
var PLAYER_HAND_FLG = 0x00000100; // 手に装備してるか
var PLAYER_HEAD_FLG = 0x00000200; // 頭に装備してるか
var PLAYER_NORMAL_FLG = 0x00000400; // 通常常態か
var PLAYER_HIT_FLG = 0x00000800; // 壁当たりがあるか
var PLAYER_ATTACK_FLG = 0x00001000; // 攻撃中か
var PLAYER_DAMAGE_FLG = 0x00002000; // ダメージがあるか
var PLAYER_WARP_FLG = 0x00004000; // ワープ中か
var PLAYER_EXIT_FLG = 0x00008000; // 出口まで到達したか
var PLAYER_DASH_FLG = 0x00010000; // ダッシュ中か
var PLAYER_SP_FLG = 0x00020000; // スペシャル使用中か
var PLAYER_FIRE_FLG = 0x00040000; // 燃えているか
var PLAYER_COLD_FLG = 0x00080000; // 凍っているか
var PLAYER_LIFE_FLG = 0x00100000; // ライフがあるか
var PLAYER_BLOW_FLG = 0x00200000; // 吹っ飛び中か
var PLAYER_ANIM_FLG = 0x00400000; // アニメーション中か
var PLAYER_STAN_FLG = 0x00800000; // 硬直中か
var PLAYER_CPU_FLG = 0x01000000; // COM操作か
var PLAYER_EXP_FLG = 0x02000000; // 爆発中か
var PLAYER_MUTEKI_FLG = 0x04000000; // 無敵中か
var PLAYER_APPEAR_FLG = 0x08000000; // 登場演出中か
var PLAYER_POEWR_FLG = 0x10000000; // パワーアップ中か
var PLAYER_BIG_FLG = 0x20000000; // 巨大化中か
var PLAYER_SMALL_FLG = 0x40000000; // 縮小化中か
var PLAYER_DEBUG_FLG = 0x80000000; // デバッグ中か
■フラグを管理するための変数を用意する
実際に、フラグを管理するための変数を用意します。
必ず 0 で初期化します。整数値で初期化しなかった場合、論理演算が使用できません。
フラグを管理するための変数を用意する
var flg = 0; // フラグ
■フラグを立てる
それではフラグを立ててみましょう。
フラグ用変数 flg に PLAYER_STAND_FLG というフラグを立ててみましょう。
フラグを立てるには、「OR演算」を使用します。
PLAYER_STAND_FLG フラグを立てる
flg |= PLAYER_STAND_FLG;
OR演算は「どちらかが 1 だったら結果は 1 」になります。
一番右のビットは、 flg がどんな値だとしても必ず 1 になります。つまりフラグが立ちます。
それ以外のビットは、flag がどんな値だとしても変化せずにそのままの状態で残ります。
PLAYER_STAND_FLG フラグを立てる |
もうひとつ例です。
フラグ用変数 flg に PLAYER_DEATH_FLG というフラグを立ててみましょう。
PLAYER_DEATH_FLG フラグを立てる
flg |= PLAYER_DEATH_FLG;
一番右から 2 番目のビットは、flg がどんな値だとしても必ず 1 になります。つまりフラグが立ちます。
それ以外のビットは、flg がどんな値だとしても変化せずにそのままの状態で残ります。
PLAYER_DEATH_FLG フラグを立てる |
このように「OR演算」を使えば、任意のビットだけを立てる事ができます。
■フラグをクリアする
次は、フラグをクリアしてみましょう。
フラグ用変数 flg から PLAYER_STAND_FLG というフラグをクリアしてみましょう。
フラグをクリアするには、「AND演算」と「NOT演算」を使用します。
「NOT演算」でビットを反転してから「AND演算」で計算します。
「NOT演算」でビットを反転してから「AND演算」で計算します。
PLAYER_STAND_FLG フラグをクリアする
flg &= ~PLAYER_STAND_FLG;
NOT演算は「ビットが反転」します。AND演算は「どちらも 1 の場合だけ結果は 1 」となります。
一番右のビットは、flg がどんな値だとしても必ず 0 になります。つまりフラグがクリアされます。
それ以外のビットは、flg がどんな値だとしても変化せずにそのままの状態で残ります。
PLAYER_STAND_FLG フラグをクリアする |
もうひとつ例です。
フラグ用変数 flg から PLAYER_DEATH_FLG というフラグをクリアしてみましょう。
PLAYER_DEATH_FLG フラグをクリアする
flg &= ~PLAYER_DEATH_FLG;
一番右から 2 番目のビットは、flg がどんな値だとしても必ず 0 になります。つまりフラグがクリアされます。
それ以外のビットは、flg がどんな値だとしても変化せずにそのままの状態で残ります。
PLAYER_DEATH_FLG フラグをクリアする |
このように「AND演算」と「NOT演算」を使えば、任意のビットだけをクリアする事ができます。
■フラグの状態を調べる
フラグが立っているか、クリアされているか、状態を調べてみましょう。
フラグ用変数 flg から PLAYER_STAND_FLG というフラグの状態を調べてみます。
フラグの状態を調べるには、「AND演算」を使用します。
PLAYER_STAND_FLG フラグの状態を調べる
if( flg & PLAYER_STAND_FLG ){
// フラグが立っている
}else{
// フラグがクリアされている
}
AND演算は「どちらも 1 の場合だけ結果は 1 」となります。
一番右のビットは、 flg がどんな値だったとしてもそのまま残ります。
それ以外のビットは、すべて 0 になります。
if() 文の条件式は、「 0 のときが false (偽)」であり「 0 以外の値のときが true (真)」なので、 0 以外のなんらかの値が取得できればフラグが立っている事がわかります。
PLAYER_STAND_FLG フラグの状態を調べる |
もうひとつ例です。
フラグ用変数 flg から PLAYER_DEATH_FLG というフラグの状態を調べてみます。
PLAYER_DEATH_FLG フラグの状態を調べる
if( flg & PLAYER_DEATH_FLG ){
// フラグが立っている
}else{
// フラグがクリアされている
}
一番右から 2 番目のビットは、flg がどんな値だったとしてもそのまま残ります。
それ以外のビットは、すべて 0 になります。
PLAYER_DEATH_FLG フラグの状態を調べる |
■フラグの操作方法まとめ
フラグの操作を行うには、以下の演算を行います。
■フラグを立てる
「任意のビットが立っている変数」と「OR演算」で計算します。
PLAYER_STAND_FLG フラグを立てる
flg |= PLAYER_STAND_FLG;
■フラグをクリアする
「任意のビットが立っている変数」を「NOT演算」でビットを反転してから、「AND演算」で計算します。
PLAYER_STAND_FLG フラグをクリアする
flg &= ~PLAYER_STAND_FLG;
■フラグの状態を調べる
「任意のビットが立っている変数」と「AND演算」で計算して、結果が 0 であればフラグがクリアされています。それ以外であればフラグが立っています。
PLAYER_STAND_FLG フラグの状態を調べる
if( flg & PLAYER_STAND_FLG ){
// フラグが立っている
}else{
// フラグがクリアされている
}
■論理演算を使うメリットとデメリット
まず、論理演算を使う場合と使わない場合と比べて、論理演算を使う方が処理が増えます。
「論理演算」という余計な計算を行うためです。
そして、論理演算を使えば、メモリ消費量が減る可能性がありますが、逆に増えてしまう可能性もあります。
論理演算用の命令を 1 回使用すると、バイトコードとして出力され swf ファイルの容量が 1 byte 増える事になります。
論理演算を使って変数の数を節約したとしても、論理演算用の命令をたくさん使ってしまうとそれだけ swf ファイルの容量が増えるので、結局実行時にメインメモリを占有する事になります。
顧客データなど配列で大量のデータを扱う場合に、for 文を使って論理演算でまとめて処理を行えばメモリ消費量の削減が見込めるでしょう。
また、セーブデータなど、とにかく容量を減らす必要がある場合に有効です。
■ビットパターンを作りまとめて比較する
論理演算を使用すると、まとめて比較する事ができます。
以下のようなジョイパッド用のビットを定義したとします。
JoyPad用に各ビットが立っている論理演算用の変数を定義する
var JOYPAD_UP = 0x00000001; // 上
var JOYPAD_DOWN = 0x00000002; // 下
var JOYPAD_LEFT = 0x00000004; // 左
var JOYPAD_RIGHT = 0x00000008; // 右
var JOYPAD_A = 0x00000010; // A
var JOYPAD_B = 0x00000020; // B
var JOYPAD_C = 0x00000040; // C
var JOYPAD_X = 0x00000080; // X
var JOYPAD_Y = 0x00000100; // Y
var JOYPAD_Z = 0x00000200; // Z
var JOYPAD_START = 0x00000400; // スタート
あらかじめ、ボタンA~Zのビットを「OR演算」を使って、変数 JOYPAD_BUTTON_ALL にまとめてみます。
変数 JOYPAD_BUTTON_ALL にまとめる
// ボタン用ビットパターン
var JOYPAD_BUTTON_ALL = JOYPAD_A | JOYPAD_B | JOYPAD_C | JOYPAD_X | JOYPAD_Y | JOYPAD_Z;
ボタンA~Zのどれかが押されているかを調べたいとします。
通常であれば条件式を 6 回書きますが、あらかじめビットのパターンを作っておけば一度の比較で判別できます。
普通に判定した場合
if( (flg & JOYPAD_A) || (flg & JOYPAD_B) || (flg & JOYPAD_C) || (flg & JOYPAD_X) || (flg & JOYPAD_Y) || (flg & JOYPAD_Z) ){
// どれかボタンが押された
}
ビットパターンを作って判定した場合
if( flg & JOYPAD_BUTTON_ALL ){
// どれかボタンが押された
}
また、ボタンA~Zのすべてが同時に押されているかを調べたいとします。
こちらも通常であれば条件式を 6 回書きますが、あらかじめビットのパターンを作っておけば少ないの比較で判別できます。
「AND演算」で該当ビットだけ抽出して、そのビットパターンが等しいかを調べます。完全一致すればすべて押された事がわかります。
普通に判定した場合
if(flg & JOYPAD_A){
if(flg & JOYPAD_B){
if(flg & JOYPAD_C){
if(flg & JOYPAD_X){
if(flg & JOYPAD_Y){
if(flg & JOYPAD_Z){
// すべてのボタンが押された
}
}
}
}
}
}
ビットパターンを作って判定した場合
if( (flg & JOYPAD_BUTTON_ALL) == JOYPAD_BUTTON_ALL ){
// すべてのボタンが押された
}
1つの変数に複数の整数値を持たせよう
■1つの変数内で複数の整数を管理する
32 bit 変数内のビットをお好みのサイズで区切って、複数の値を管理する事ができます。
1 bit たりとも無駄にせずに、効率よくデータを保持するために使われる手法です。
32bit変数の各ビットを好みのサイズで区切って複数の値を管理する
00100111 | 0001 | 00001000 | 00 | 10 | 0001 | 0011
↑ ↑ ↑ ↑ ↑ ↑ ↑
39 1 8 0 3 1 3
■表現できる上限に注意する
ビット数を少なくするほど、表現できる範囲が狭くなる事に注意します。
例えば、 1 ビットだと「 0 から 1 までの 2 パターン」しか表現できません。4 ビットだと「 0 から 15 までの 16 パターン」しか表現できません。
あらかじめ仕様をがっちり決めておかないと、後で範囲を修正したくなっても対応が難しくなります。
ビット数 | 範囲 | パターン数 |
1 | 0 ~ 1 | 2 |
2 | 0 ~ 3 | 4 |
3 | 0 ~ 7 | 8 |
4 | 0 ~ 15 | 16 |
5 | 0 ~ 31 | 32 |
6 | 0 ~ 63 | 64 |
7 | 0 ~ 127 | 128 |
8 | 0 ~ 255 | 256 |
9 | 0 ~ 511 | 512 |
10 | 0 ~ 1023 | 1024 |
11 | 0 ~ 2047 | 2048 |
12 | 0 ~ 4095 | 4096 |
13 | 0 ~ 8191 | 8192 |
14 | 0 ~ 16383 | 16384 |
15 | 0 ~ 32767 | 32768 |
16 | 0 ~ 65535 | 65536 |
17 | 0 ~ 131071 | 131072 |
18 | 0 ~ 262143 | 262144 |
19 | 0 ~ 524287 | 524288 |
20 | 0 ~ 1048575 | 1048576 |
21 | 0 ~ 2097151 | 2097152 |
22 | 0 ~ 4194303 | 4194304 |
23 | 0 ~ 8388607 | 8388608 |
24 | 0 ~ 16777215 | 16777216 |
25 | 0 ~ 33554431 | 33554432 |
26 | 0 ~ 67108863 | 67108864 |
27 | 0 ~ 134217727 | 134217728 |
28 | 0 ~ 268435455 | 268435456 |
29 | 0 ~ 536870911 | 536870912 |
30 | 0 ~ 1073741823 | 1073741824 |
31 | 0 ~ 2147483647 | 2147483648 |
32 | 0 ~ 4294967295 | 4294967296 |
■8 ビットずつ区切って 4 つの変数を扱う
今回は 8 ビットずつ区切って 4 個の入れ物に分けてみましょう。
32bit変数の各ビットを 8 bit で区切って 4 つの値を管理する
0 0 1 0 0 1 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 1 1
使用例です。
■一番右の 8 ビットに数値を入れる
以下のような「一番右の 8 ビット」の領域を使用して数値を入れてみましょう。
一番右の 8 ビットを使用する |
まず変数を用意します。ここでは、
管理する変数を hensu とします。
8 ビットとして扱う変数を data とします。
論理演算として計算できるように必ず 0 などの整数値で初期化します。
管理する変数を hensu とします。
8 ビットとして扱う変数を data とします。
変数を用意する
var hensu = 0;
var data = 0;
検証用に各変数に適当な値を代入します。
検証用に適当な値を入れておく
hensu = 0x12345678;
data = 0xEF;
変数 hensu に書き込む予定の領域が 0 になるよう AND 演算を使用してクリアします。
AND 演算で、右端 8 ビットをクリアする
hensu &= 0xFFFFFF00;
一番右の 8 ビットをクリアする |
変数 hensu と変数 data を OR 演算して 変数 hensu に書き込みます。
変数 data を OR 演算で変数 hensu に書き込む
hensu |= data;
変数 data を OR 演算で変数 hensu に書き込む |
変数 data の値が、「 8 ビットで表現できる上限より大きい」などの理由により、
「下位 8 ビット以外のビットが 1 である」可能性があるなら、いったん AND 演算で下位 8 ビット以外のビットをすべてゼロでクリアしておくと安全です。
変数 data の下位 8 ビットを AND 演算でクリアしてから、
変数 data を OR 演算で変数 hensu に書き込む
変数 data を OR 演算で変数 hensu に書き込む
hensu |= (data & 0x000000FF);
trace() 関数で中身を表示してみると、下位 8 ビットに値が格納されたことがわかります。
トレース関数で表示してみる
var hensu = 0x12345678;
var data = 0xEF;
hensu &= 0xFFFFFF00;
hensu |= (data & 0x000000FF);
trace((hensu).toString(16)); // 123456ef
■一番右の 8 ビットから値を取り出す
以下のような「一番右の 8 ビット」の領域を使用して数値を取り出してみましょう。
一番右の 8 ビットを使用する |
まず変数を用意します。ここでは、
管理する変数を hensu とします。
8 ビットとして扱う変数を data とします。
論理演算として計算できるように必ず 0 などの整数値で初期化します。
管理する変数を hensu とします。
8 ビットとして扱う変数を data とします。
変数を用意する
var hensu = 0;
var data = 0;
検証用に各変数に適当な値を代入します。
検証用に適当な値を入れておく
hensu = 0x12345678;
変数 hensu の下位 8 ビット以外のビットを AND 演算を使ってクリアします。
そのまま変数 data に代入します。
変数 hensu の下位 8 ビット以外をクリアして変数 data に代入
data = (hensu & 0x000000FF);
一番右の 8 ビット以外をクリア |
trace() 関数で中身を表示してみると、下位 8 ビットの値が得られたことがわかります。
トレース関数で表示してみる
var hensu = 0x12345678;
var data = 0;
data = (hensu & 0x000000FF);
trace((data).toString(16)); // 78
■一番右から 17 ~ 24 ビットに値を入れる
以下のような「右から 17 ~ 24 ビット」の領域を使用して数値を入れてみましょう。
右から 17 ~ 24 ビットを使用する |
まず変数を用意します。ここでは、
管理する変数を hensu とします。
8 ビットとして扱う変数を data とします。
論理演算として計算できるように必ず 0 などの整数値で初期化します。
管理する変数を hensu とします。
8 ビットとして扱う変数を data とします。
変数を用意する
var hensu = 0;
var data = 0;
検証用に各変数に適当な値を代入します。
検証用に適当な値を入れておく
hensu = 0x12345678;
data = 0xEF;
変数 hensu に書き込む予定の領域が 0 になるよう AND 演算を使用してクリアします。
AND 演算で、右から 17 ~ 24 ビットをクリアする
hensu &= 0xFF00FFFF;
右から 17 ~ 24 ビットをクリアする |
変数 data を左に 16 回シフト演算します。
変数 data を左に 16 回シフト演算する
data <<= 16;
変数 data を左に 16 回シフト演算する |
変数 data の値が、「 8 ビットで表現できる上限より大きい」などの理由により、
「下位 8 ビット以外のビットが 1 である」可能性があるなら、いったん AND 演算で下位 8 ビット以外のビットをすべてゼロでクリアしておくと安全です。
変数 data の下位 8 ビットを AND 演算でクリアしてから、
変数 data を左に 16 回シフト演算する
変数 data を左に 16 回シフト演算する
data = (data & 0x000000FF) << 16;
変数 hensu と変数 data を OR 演算して 変数 hensu に書き込みます。
変数 data を OR 演算で変数 hensu に書き込む
hensu |= data;
変数 data を OR 演算で変数 hensu に書き込む |
trace() 関数で中身を表示してみると、右から 17 ~ 24 ビットに値が格納されたことがわかります。
トレース関数で表示してみる
var hensu = 0x12345678;
var data = 0xEF;
hensu &= 0xFF00FFFF;
hensu |= (data & 0x000000FF) << 16;
trace((hensu).toString(16)); // 12ef5678
■一番右の 17 ~ 24 ビットから値を取り出す
以下のような「右から 17 ~ 24 ビット」の領域を使用して数値を取り出してみましょう。
右から 17 ~ 24 ビットを使用する |
まず変数を用意します。ここでは、
管理する変数を hensu とします。
8 ビットとして扱う変数を data とします。
論理演算として計算できるように必ず 0 などの整数値で初期化します。
管理する変数を hensu とします。
8 ビットとして扱う変数を data とします。
変数を用意する
var hensu = 0;
var data = 0;
検証用に各変数に適当な値を代入します。
検証用に適当な値を入れておく
hensu = 0x12345678;
変数 hensu を右に 16 回算術シフト演算します。
変数 hensu を右に 16 回算術シフト演算
hensu = hensu >>> 16;
右に 16 回算術シフト |
変数 hensu の下位 8 ビット以外のビットを AND 演算を使ってクリアします。
そのまま変数 data に代入します。
下位 8 ビット以外をクリアして変数 data に代入
data = hensu & 0x000000FF;
一番右の 8 ビット以外をクリア |
trace() 関数で中身を表示してみると、「右から 17 ~ 24 ビット」の値が得られたことがわかります。
トレース関数で表示してみる
var hensu = 0x12345678;
var data = 0;
data = (hensu >>> 16) & 0x000000FF;
trace((data).toString(16)); // 34