りょうのゲーム塾 -113ページ目

りょうのゲーム塾

再開、準備中。

チェックポイント
コンピュータには2進数が都合がいいのは分かっていただけたと思います。
実際にプログラムの中でもハード(コンピュータや周辺の機械)よりのプログラムになると特にこの2進数が必要になってきます。
しかし我々が普段計算するのは10進数なのでその間で数値の変換を行うわけです。

ですが10進数だと2進数がピンとこないし、
2進数だと0と1の羅列なので実際に合ってるんだか間違ってるんだかわからないことが多く。
一つの値を指定するのに32ビットの場合には32桁必要です。(要するに冗長だということです)
この2進数の良さを残しもう少し分かりやすい表現方法があれば非常に助かると思いませんか?

そこで登場するのが16進数です。


今回のサンプルゲームでもBUTTON関数の値をマスクするのには2進数で考えた方が楽です。
例えばAボタンが押されたかどうかを判断するのに16という値が使用されていました。
しかしこの16の本来の意味はBUTTON関数が返す値がBOTTONのハードウェアの状態を1ビットで表し、
すべてのボタンの情報をまとめて返すため、必要なBOTTONの情報だけを抜き出す作業としてマスク処理を行う必要があり、
そのビット位置を指定するための &B000000010000 という値が必要だったからです。
これをそのまま2進数で使った方が10進数より分かりやすわけです。
しかしこれで本当に正しいのでしょうか?

2進数だと1が右に一つずれていても分かりにくためバグを生み出す危険性が残ります。
そこで10進数と2進数の間の表現として16進数を使います。

では16進数とはどんなものかですね。
当然、16で1桁上がりする数が16進数です。
では数えてみましょう。

0、1,2,3,4,5,6,7,8,9・・・ん?

次は10だけど・・・これでは桁上げしてますから一桁で表わさなければなりません。
皆さんは9の次の数字知ってますか?

実は私も高校2年になるまで0~9しか知りませんでした。

まぁ、結果として数字は9以上はないんです。

ないので9の次はAです。

ではもう一度数えてみます。

0、1,2,3,4,5,6,7,8,9,A,B,C,D,E,F 

これで15まで一桁で数えることができました。
次は16なので桁上げしていいので10となります。

ここで2進数と16進数と10進数の関係をまとめてみます。
2進数をバイナリといい、今後BINと表します。
16進数はヘキサデシマルといい、今後HEXと表します。
10進数はデシマルといい、今後DECと表します。

BIN HEX DEC
0000 0 0
0001 1 1
0010 2 2
0011 3 3
0100 4 4
0101 5 5
0110 6 6
0111 7 7
1000 8 8
1001 9 9
1010 A 10
1011 B 11
1100 C 12
1101 D 13
1110 E 14
1111 F 15

2進数から10進数へ変換したり、
10進数から2進数へ変換する方法は説明しました。
まぁ、はっきり言って変換するのは面倒です。

しかし、16進数はどうでしょうか?
2進数から16進数へ変換するには2進数を4桁毎に上の表から選んで書き換えればいいわけですよね。
16進から2進数も同様です。

では16進数から10進数へはどうするか?
方法は2進数から10進数へと変換するのと同じです。
掛ける重みが16のべき乗となります。

1桁目の重みは・・・16^0・・・1
2桁目の重みは・・・16^1・・・16
3桁目の重みは・・・16^2・・・256
n桁目の重みは・・・16^(n-1)

それに16進数1桁の値を掛けてすべての値を加算します。

たとでば16進数23を10進数に変換すると。。。

1桁目・・・重み1×16進数1桁の値3・・・3
2桁目・・・重み16×16進数1桁の値2・・・32
合計して32+3=35

答えは35となります。

10進数から16進数へは16で割った余りを2進数のときと同じように処理すれば変換できます。


じゃ、10進数と16進数の変換では2進数のときと同じかと思われますが、
明らかに桁数が4分の1なので計算量が違います。
これだけでもメリットになります。
チェックポイント
ここでまたプログラムから離れますが、コンピュータが扱うデータのことをある程度知っておかなければ自由にプログラムを作ることはできません。
今回は2進数で負の数を表現する方法について解説します。



プチコンで扱う数値データは32ビットの2進数を使用しています。
以前にも書きましたがコンピュータは2進数で物事を処理しています。
しかし、実際に我々の身の回りにあるデータはいろいろな形の情報が存在します。
なので我々プログラマは情報をコンピュータで認識できるデータに変換する必要があります。
例えば負の数もそのひとつです。

プチコンでは32ビットの2進データのうち20ビットを整数部で使い、12ビットを小数部で使っています。

これは10進表現のデータだけです。

どうもプチコンでは10進以外に2進数と16進数が使えるのですが、この表現の場合は整数部しか扱えないようです。
(そう感じているだけですけど、今後調べてみます。また16進数については後で書くとしましょう。)

ここでは小数部分を考えずに整数部分でのみ考えてみたいと思います。
要するに2進数20ビットで考えてみます。

前回0のNOT(否定)で-1になりました。
0は2進数20ビットで表現すると…

00000000000000000000

NOTは0が1に、1が0になります。
そうすると0をNOTすると…

11111111111111111111

これが-1なのです。

そこで前回も書きましたが先頭のビットを符号ビットとかサインビットと呼びます。
このビットが0のとき正の数を表し、1のとき負の数を表します。

20ビットであらわせる値は先頭のビットが符号ビットとして使われると正の数(0も含めて)の範囲は

00000000000000000000 から 01111111111111111111 までです。

(10進数では0~524287です)

負の数の範囲はどうなるかですが…
現在わかっていることが 11111111111111111111 が-1だということです。

まずなんでこの値が-1何でしょうか?

でも答えは簡単なのです。
もしこの値が-1と仮定しましょう。
2進数でも加算はわかりますよね?
減算より加算の方が分かり易いので次の計算をやってみましょう。

まず10進数で検証します。
この値が-1であることを簡単に実証するには

1-1=0

を実証してみればいいわけです。
これは減算ですがこの式を

1+(-1)=0

と置き換えればいいわけです。
では2進数の計算です。

00000000000000000001
+11111111111111111111
─────────────────────

では一番下の桁から計算します。
1と1を足すと0で1桁上げします。

1 ←桁上げ
00000000000000000001
+11111111111111111111
─────────────────────
0

次の桁も桁上げの1と0と1を足すので0で桁上げ1となります。

1 ←桁上げ
00000000000000000001
+11111111111111111111
─────────────────────
00

最後の桁まで計算すると

1 ←桁上げ
00000000000000000001
+11111111111111111111
─────────────────────
00000000000000000000

最後こ桁からの桁上げですが有限桁での計算ではこの桁上げは数値には反映されません。
この桁上げをキャリーと呼びます。
覚えておいていただければと思います。

どうでしょうか?
結果が0になりましたね?

だから 11111111111111111111 は-1であることが理解できますね。
それ以外の負の数はどうやって計算しましょうか?

大丈夫です。
符号反転の方法をここで説明します。
符号反転は1ならば-1にすることです。
この方法は元の値をNOTして1を加えるという計算です。

では今度は1の符号反転を行ってみましょう。
まず1をNOTします。

00000000000000000001
─────────────────────
11111111111111111110

これに1を足します。

11111111111111111110
+00000000000000000001
─────────────────────
11111111111111111111

となります。
これは-1ですね。

これで計算するといいわけです。

11111111111111111111 -1
11111111111111111110 -2
11111111111111111101 -3
11111111111111111100 -4

(中略)

10000000000000000001 -524287
10000000000000000000 -524288

ただ・・・プチコンではなんと最後の値が-0という表示になりますが、仕様なので仕方ありません。


この様に2進数の符号反転はNOTし1を足す方法で求めることができます。
この方法を2の補数と呼びます。

チェックポイント
DSiのボタン情報を取得するとき AND 演算子を使用しました。
この処理をマスク処理と呼びます。(過去記事参照のこと)

論理演算を使うことで意外と面倒な処理を比較的簡単に解決することができます。
そのためにも論理演算がどんな演算なのかを知っておきましょう。


論理演算は2進数1桁毎の演算です。
2進数は0と1の2つの値しかありません。
その演算の組み合わせは4通りしかありません。
では論理演算はどういうものか見てみましょう。

■OR(論理和)
a
b
a OR b
0
0
1
1
0
1
0
1
0
1
1
1

もともと論理とはその条件が成立するのかしないのかを計算するもので条件が成立する事を真または true と表し、2進数では1となります。
逆に成立しないことを偽または false と表し、2進数では0となります。
論理和は2つの条件がどちらか一方が成り立つ場合に真となります。
上のような論理演算の表を真理値表といいますが、その表を見てください。
a の条件と b の条件のどちらか一方が成り立てば真(1)となります。

■AND(論理積)
a
b
a AND b
0
0
1
1
0
1
0
1
0
0
0
1

論理積は2つの条件が共に成り立つ場合真(1)となります。

■XOR(排他的論理和)
a
b
a XOR b
0
0
1
1
0
1
0
1
0
1
1
0

排他的論理和は論理和に近いのですが2つの条件のどちらか一方のみ成立する場合に真(1)となります。論理和は両方共に成立しても真ですが、排他的論理和は片方のみ真のとき真となります。

では実際にそれが本当なのか実際にプログラミングしてみましょう。
論理演算は2進の各桁で計算されます。
上の真理値表の a は上から 0011 です。
b は上から 0101 です。
ということはこの表の計算を一度に計算するのですから

0011 OR 0101
0011 AND 0101
0011 XOR 0101

でいいわけです。
計算は2進で計算されますが結果が10進で表示されるので変換表も表示しておきます。

あ、もうひとつ論理演算に NOT があります。
これは否定です。
真(1)だったら偽(0)へ、偽(0)だったら真(1)へ反転します。

■変換表
10進数
2進数
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111


■プログラム
0001 ' sample program
0002 A = &B0011
0003 B = &B0101
0004 ANS_OR = A OR B
0005 ANS_AND = A AND B
0006 ANS_XOR = A XOR B
0007 PRINT "OR=";ANS_OR
0008 PRINT "AND=";ANS_AND
0009 PRINT "XOR=";ANS_XOR
0010 PRINT "NOT=";NOT 0
0011 END


実行結果は?

0011 OR 0101 = 0111 = 7
0011 AND 0101 = 0001 = 1
0011 XOR 0101 = 0110 = 6

だから

OR=7
AND=1
XOR=6

になりましたか?

問題は NOT なんですが0の反転は・・・1なのに・・・答えは-1でした

本来0とは2進数でプチコン内部では32ビットの固定少数点方式です。
整数部分は20ビット(だったかなぁ?)小数部は12ビットで下記のようなものです。

00000000000000000000.000000000000

しかし、論理演算で整数で扱えば20ビットの値と同じです(たぶん^^;)

ですので0は2進数で

00000000000000000000

です。
これを NOT すると

11111111111111111111

となります。

実は数値は負の値も持っています。
ちなみに一番左のビットはサイン(符号)ビットともいいます。
0のとき正の数で、1のとき負の数です。

じゃ、正の1は

00000000000000000001

だから負の-1は

10000000000000000001

では?
って、たぶん賢いあなただったら思うはず!!

けど現実は違って

11111111111111111111

が-1なのです。

では次回なぜそうなのか解説したいと思います。