3-7 演算子(その2)

2進数が理解できたところで。2進数演算、ビット演算、シフト演算を理解しましょう。

2進数演算

2進数演算っといっても特に2進数だからと意識する必要はありません。
10進数とは単に表示方法が違うだけで、表している数値は変わりませんから計算方法も変わりません。8進数や16進数も同じです。
次のプログラムを見てください。

 1 #include <stdio.h>
 2
 3 void bin_print(int, int , char[]);
 4
 5 /*********************************************
 6  * サンプルプログラム                        *
 7  *********************************************/
 8 int main()
 9 {
10     int dec;
11     int keta=4;
12     char ba[keta], bb[keta], bc[keta];
13     int a, b, c;
14
15     a = 5;
16     b = 4;
17     c = a + b;
18     printf("10進数  a=%d  b=%d  c=%d  です\n",a,b,c);
19
20     //a b c を2進数表示にする
21     bin_print(a, keta, ba);
22     bin_print(b, keta, bb);
23     bin_print(c, keta, bc);
24     printf(" 2進数  a=%s  b=%s  c=%s  です\n",ba,bb,bc);
25
26     return 0;
27 }
28
29
30 void bin_print(int dec, int keta, char binc[])
31 {
32 /*********************************************
33  * 整数を2進数の文字列に変換する             *
34  * (INPUT)   dec   入力値の10進数            *
35  * (INPUT)   keta  2進数に変換する桁数       *
36  * (OUTPUT)  binc  2進数の文字列             *
37  *********************************************/
38     unsigned char cw;
39
40     for (int i=0; i<keta; i++) {
41         cw = (dec >> i) & 0x01;
42         binc[(keta-1)-i] = (cw > 0) ? '1' : '0';
43     }
44     binc[keta] = 0x0;
45     return;
46 }
実行結果
10進数  a=5  b=4  c=9  です
 2進数  a=0101  b=0100  c=1001  です

17行目を見てください。演算の式はこれだけです。
10進数の表示は18行目で、2進数の表示は24行目です。
単に表示の方法が違うだけなのです。
(上のサンプルプログラムを人口する場合は、コピペでprg.cファイルに張り付けて行番号を消すと実行できますよ)

ビット演算子

ビット(bit)演算とはある数を2進数で計算する処理の事を言います。
ハードウェアの制御や通信処理、グラフィック処理などで多く使用されます。
普通のプログラムでも使用しますが、データ量を削減したり計算コストを小さくしたり、また2進数に固有の演算に使用されます。

私の経験上では、一般的な用途で使用する機会は非常に少ないと思います。
でも重要な概念ですので、こんな感じかと頭の隅に置いておいてください。
理屈は本当に簡単ですので。
必要になった時に、「あんな機能があったな」と思い出せたら良いと思います。
ただ後程説明しますが、マスクという考えはよく使用されます。

AND (論理積)

a AND b はプログラムでは、
a&b
と書き、言葉では aかつb という意味です。
2つのいずれも真のときに真となり、それ以外のときは偽となります。
真とは1の事で、偽は0の事です。

4bitの例を示します。
c = a&b; ←プログラムの式(記号:アンパサンド
a が 1010
b が 0110
ならば
c は 0010
となります。
a も b も 1 のbitだけが 1 となります。
a、b、c のbitを縦に並べてみてみてください。

OR(論理和)

a OR b はプログラムでは、
a|b
と書き、言葉では aまたはb という意味です。
2つのいずれかが真であれば真となり、それ以外は偽となります。

4bitの例を示します。
c = a|b; ←プログラムの式(記号:バーティカルバー、縦線)
a が 1010
b が 0110
ならば
c は 1110
となります。
a と b のいずれかが 1 のbitが 1 となります。
a、b、c のbitを縦に並べてみてみてください。

XOR(排他的論理和)

a XOR b はプログラムでは、
a^b
と書き、残念ながら表す言葉はありません。
2つのいずれか一方のみが真のときに真となり、両方真や両方偽のときは偽となります。

4bitの例を示します。
c = a^b; ←プログラムの式(記号:ハット)
a が 1010
b が 0110
ならば
c は 1100
となります。
a と b のどちらかが 1 のbitが 1 となります。
a、b、c のbitを縦に並べてみてみてください。
実はこのXORはマウスのラバーバウンドを背景色と絶対に一致しない色にするのに利用できたりするのです。

NOT(否定)

NOT a はプログラムでは、
~a
と書き、言葉では bit反転です。
0 のbitは 1 に、1 のbitは 0 にします。

4bitの例を示します。
c = ~a; ←プログラムの式(記号:チルダ)
a が 1010
ならば
c は 0101
となります。
a の 0 のbitが 1 となり、1 のbitが 0 となります。

まとめ

上記で説明したビット演算を表にまとめてみましょう。
1bit毎の演算のまとめです。これをすべてのbitに対して行います。

abAND
a & b
OR 
a | b
XOR
a ^ b
00000
01011
10011
11110

シフト演算子

シフト演算とは、2進数のbit列を左または右にずらす操作のことです。

右シフト演算子

2進数のbit列を右へずらす演算子です。

a を右へ2bitずらすにはプログラムでは、
a>>2
と書きます。

8bitの例を示します。
c = a>>2; ←プログラムの式(記号:不等号(より大))
a が 10111010
ならば
c は 00101110
となります。
右に2bitずらして、右側にあふれたビットは削除され、左側に0が追加されます。
しかし負の数に対して右シフトを行った場合に、0が追加されるか、1が追加されるかは処理系によって異なります。

ここで、1bit右シフトするとは数学的にはどんな意味があるのでしょう?
数字の2を2進数にすると、4bitでは
2 ⇒ 0010
です。
これを1bit右シフトします。
2 1bit右シフト ⇒ 0001
この 0001 は10進数では 1 です。
1bit右シフトすると数値は2分の1になるのです。
2bit右シフトすると数値は4分の1になるのです。ここでは右にはみ出て0ですが。

左シフト演算子

2進数のbit列を左へずらす演算子です。

a を左へ2bitずらすにはプログラムでは、
a<<2
と書きます。

8bitの例を示します。
c = a<<2; ←プログラムの式(記号:不等号(より大))
a が 10111010
ならば
c は 11100100
となります。
左に2bitずらして、左側にあふれたビットは削除され、右側に0が追加されます。

ここで、1bit左シフトするとは数学的にはどんな意味があるのでしょう?
数字の2を2進数にすると、4bitでは
2 ⇒ 0010
です。
これを1bit左シフトします。
2 1bit左シフト ⇒ 0100
この 0100 は10進数では 4 です。
1bit左シフトすると数値は2倍になるのです。
2bit左シフトすると数値は4倍になるのです。

2進数のプログラム例

ここまでに説明した2進数の演算の例をあげて説明します。

 1 #include <stdio.h>
 2
 3 void bin_print(int, int , char[]);
 4
 5 /*********************************************
 6  * bit演算サンプルプログラム                 *
 7  *********************************************/
 8 int main()
 9 {
10     int dec;
11     int keta=4;
12     char ba[keta], bb[keta], bc[keta];
13     int a, b, c;
14
15     // a に2進数値 1010 のをセット
16     // 0b は 2進数を表す接頭語です
17     // 0  は 8進数を表す接頭語です
18     // 0x は16進数を表す接頭語です
19     a = 0b1010;
20     b = 0b0110;
21
22     /**********************
23      * ADN の例題
24      * 1010 and 0110
25      **********************/
26     c = a & b;
27
28     //a b c を2進数表示にする
29     bin_print(a, keta, ba);
30     bin_print(b, keta, bb);
31     bin_print(c, keta, bc);
32
33     printf("--- AND sample ---\n");
34     printf(" 2進数では  a=%s  b=%s  c=%s  です\n",ba,bb,bc);
35
36     /**********************
37      * OR の例題
38      * 1010 or 0110
39      **********************/
40     c = a | b;
41
42     //a b c を2進数表示にする
43     bin_print(a, keta, ba);
44     bin_print(b, keta, bb);
45     bin_print(c, keta, bc);
46
47     printf("--- OR  sample ---\n");
48     printf(" 2進数では  a=%s  b=%s  c=%s  です\n",ba,bb,bc);
49
50     /**********************
51      * XOR の例題
52      * 1010 xor 0110
53      **********************/
54     c = a ^ b;
55
56     //a b c を2進数表示にする
57     bin_print(a, keta, ba);
58     bin_print(b, keta, bb);
59     bin_print(c, keta, bc);
60
61     printf("--- XOR sample ---\n");
62     printf(" 2進数では  a=%s  b=%s  c=%s  です\n",ba,bb,bc);
63
64     /**********************
65      * NOT の例題
66      * not 1010
67      **********************/
68     // a に2進数値 1010 のをセット
69     c = ~a;
70
71     //c を2進数表示にする
72     bin_print(a, keta, ba);
73     bin_print(c, keta, bc);
74     printf("--- NOT sample ---\n");
75     printf(" 2進数では  a=%s  c=%s  です\n",ba,bc);
76
77     return 0;
78 }
79
80
81 void bin_print(int dec, int keta, char binc[])
82 {
83 /*********************************************
84  * 整数を2進数の文字列に変換する             *
85  * (INPUT)   dec   入力値の10進数            *
86  * (INPUT)   keta  2進数に変換する桁数       *
87  * (OUTPUT)  binc  2進数の文字列             *
88  *********************************************/
89     unsigned char cw;
90
91     for (int i=0; i<keta; i++) {
92         //右に ibitシフトして 01でマスクして最下位bitを取得する
93         cw = (dec >> i) & 0x01;
94         binc[(keta-1)-i] = (cw > 0) ? '1' : '0';
95     }
96     binc[keta] = 0x0;
97     return;
98 }

実行結果
--- AND sample ---
 2進数では  a=1010  b=0110  c=0010  です
--- OR  sample ---
 2進数では  a=1010  b=0110  c=1110  です
--- XOR sample ---
 2進数では  a=1010  b=0110  c=1100  です
--- NOT sample ---
 2進数では  a=1010  c=0101  です

15行~20行をみてください。
数値を入れるときに10進数以外の場合には数値に接頭語が必要です。
こんな感じです。
a = 8; ←10進数の8を入れる
a = 0b101; ← 2進数の101を入れる
a = 0370; ← 8進数の370を入れる
a = 0x7ec; ← 2進数の7ecを入れる
プログラムの結果と説明を見比べてください。

91行からの関数を説明します。
しかし、まだ関数は説明していないのですが、ご勘弁を、流してくださいね。
for 文もまだ学んでいませんので、これもご勘弁。
難解だったら、飛ばしてください。

ここで、93行~94行だけに注目してください。これが2進数演算なのです。
cw = (dec >> i) & 0x01;
これは2つの演算の集まりです。
1つは、
dec >> i;
これは10進数が入っているdec変数を、順にi bit右へシフトする計算です。
その結果に、
& 0x1
します。
これは16進数の1をANDしています。
これをマスク処理といいます。
全ビット中の一部分だけが見えるようにマスキングするという意味です。
ここでは右の1bitだけが欲しいためのマスク処理です。

具体例で、10進数の10を2進数に変換する場合を考えます。
10進数の10は、2進数では 1010 です。
16進数の1は、2進数では 0001 です。
始めの状態は
dex = 0b1010
です。
ここで以下の4回の処理を行うことで10進数を2進数文字列に変換します。

<1回目>
decを0bit右にシフトします。
dec ⇒ 1010
16進数の1をandします。
1010 & 0001 ⇒ 0
この結果の0は dex(1010) の1番右のbitを取ったことになります。

<2回目>
decを1bit右にシフトします。
dec ⇒ 0101
16進数の1をandします。
0101 & 0001 ⇒ 1
この結果の0は dex(1010) の右から2番目のbitを取ったことになります。

<3回目>
decを2bit右にシフトします。
dec ⇒ 0010
16進数の1をandします。
0010 & 0001 ⇒ 0
この結果の0は dex(1010) の右から3番目のbitを取ったことになります。

<4回目>
decを3bit右にシフトします。
dec ⇒ 0001
16進数の1をandします。
0001 & 0001 ⇒ 1
この結果の0は dex(1010) の右から4番目のbitを取ったことになります。

この4回の処理でdecの2進数のbit列をシフトを使うことで、1つずつ取り出しているだけなのです。

そして、この1回目~4回目の結果の1,0が、93行目のcwにセットされます。
これを2進数ではなく、人間の目で分かる文字の0,1に直しているのが94行目の、
binc[(keta-1)-i] = (cw > 0) ? '1' : '0';
です。
この文の右辺は、cwが正であれば文字の1を、そうでなければ文字の1をセットしなさいということです。ここで2進数を目に見える形の文字列の0,1に変換しているのです。
また配列はまだ学習していませんので、イコールの左辺は無視してください。

なんとなくの感触、つかんでいただけたでしょうか。
なんか???でも、無視して進めていきましょう。
いずれ分かりますので、ご心配なく。
(上のサンプルプログラムを実行する場合は、コピペでprg.cファイルに張り付けて行番号を消すと実行できますよ)


コメント

タイトルとURLをコピーしました