.net column
.NET開発者のためのブログメディア

C#のビット演算の方法を解説|ビットフラグのメリットや例も紹介

 
C#のビット演算の方法を解説|ビットフラグのメリットや例も紹介
SE
C#の<<について、詳しく教えていただけませんか。
PM
わかりました。それでは、C#の<<について解説いたしましょう。

C#のビット演算とは


C#のビット演算とは、何なのでしょうか?

私たちが計算するときは、ほぼ全ての人が10進数で考えます。0~9で数を表し、10になれば次の桁を使います。

ところが、コンピューターは2進数しか理解できません。0と1だけで数を表すので、1010011…のように表現します。この2進数を使って計算することをビット演算と言います。

C#等の高級言語では、2進数だけでは人間が理解しにくいので10進数や16進数も扱えるように、言語側で変換してくれています。

そのため、ビット演算について理解していなくてもC#のプログラムを作ることはできてしまいます。しかし、コンピューターは2進数で動作しているので2進数で計算しやすい処理にしてあげることで処理が高速になったり、メモリを節約したりすることができるようになります。

ビット演算の種類

C#のビット演算には、大きく分けて2つの種類があります。
 ・シフト演算
 ・論理演算
それぞれの演算について、詳しく解説していきます。

また、C#におけるビット演算の実用性についても説明します。

シフト演算

シフト演算とはビット列全体を右、または左へ移動させる演算のことです。

C#のシフト演算には
 ・左シフト演算(演算子:<<)  ・右シフト演算(演算子:>>)
があります。

シフト演算で注意しなければならないのは『範囲外に出るビットの扱い』と『範囲内に繰り入れられるビットの値』です。

C#では、ビット演算をしようとしている変数の型によって、『範囲外に出るビットの扱い』と『範囲内に繰り入れられるビットの値』が変わりますので、注意してください。

論理演算

論理演算とはビット列同士の足し算や掛け算のことです。

C#の論理演算には
 ・論理AND演算(論理積とも呼ばれる)(演算子:&)
 ・論理OR演算(論理和とも呼ばれる)(演算子:|)
 ・論理XOR演算(排他的論理和とも呼ばれる)(演算子:^)
 ・補数演算(NOT演算・反転とも呼ばれる)(演算子:~)
があります。

C#におけるビット演算の実用性

<<をはじめとするビット演算子について紹介しましたが、実際にどのように役に立つのでしょうか。先に述べたように演算の高速化やメモリの節約に役立ちます。 しかし今のコンピュータではビット演算による高速化や節約は効果が少なく、それほど意味がありません。昔のコンピュータはメモリが少なく、少しでも高速化やメモリを節約する必要があったので、このようなビット演算が使われていたのです。

C#のビット演算に関わる演算子の使い方6つ


ここからはC#の6つのビット演算について、以下の順番で紹介していきます。
 ・左シフト演算(演算子:<<)  ・右シフト演算(演算子:>>)
 ・論理AND演算(論理積とも呼ばれる)(演算子:&)
 ・論理OR演算(論理和とも呼ばれる)(演算子:|)
 ・論理XOR演算(排他的論理和とも呼ばれる)(演算子:^)
 ・補数演算(NOT演算・反転とも呼ばれる)(演算子:~)

1:左シフト演算子について

左シフト演算子はビット列全体を左へ移動させる演算で、C#での演算子は『<<』です。

まずは、以下のC#のサンプルプログラムをご覧ください。

実行結果は次のようになります。

サンプルプログラムについて、上から順に解説していきます。

まず『uint』とは『unsigned int』のことで、正の数の整数型です。2進数ではこの型を使うとわかりやすくなります。

次に、C#では先頭に『0b』を付けることで2進数を表現します。『0b0001』は2進数の「0001」、つまり1ということになります。

次の『i = i << 1;』で、iを左に1ビットシフトします。iは「0001」でしたので、1ビット左にシフトすると「0010」になります。『i = i << 1;』は『i <<= 1』と書き換えることもできます。 次の『System.Console.WriteLine("10進数で表現:"+i);』で結果を表示します。iをそのまま出力すると10進数で表示されます。 iを2進数で表示したい場合は『System.Console.WriteLine("2進数で表現:"+Convert.ToString(i, 2));』としてください。

2:右シフト演算子について

右シフト演算子はビット列全体を右へ移動させる演算で、C#での演算子は『>>』です。

以下のC#サンプルをご覧ください。

実行結果は、
になります。

今度は、iが「0100」なので、1ビット右にシフトすると「0010」になります。左シフトと同様に『i = i >> 1;』は『i >>= 1;』と書き換えることもできます。

3:論理AND演算子について

論理AND演算子はビットごとの論理積を求める演算で、C#での演算子は『&』です。

以下のC#サンプルをご覧ください。

実行結果は、

となります。つまり0100ですね。

&は2進数の各桁で掛け算を行います。

このサンプルの場合、上の桁から

により、0100となるのです。

4:論理OR演算子について

論理OR演算子はビットごとの論理和を求める演算で、C#での演算子は『|』です。

以下のC#サンプルをご覧ください。先ほどのC#サンプルの&を、|に変えました。

実行結果は、

になります。

|は各桁の加算を行います。ただし1と1を加算した場合は1のままです。

このサンプルの場合、上の桁から

と演算され、1101となりました。

5:論理XOR演算子について

論理XOR演算子は各桁を比較し違う値であれば1、同じであれば0にする演算で、C#での演算子は『^』です。

以下のC#のサンプルプログラムをご覧ください。

実行結果は

になります。

^は各桁を比較し違う値であれば1、同じであれば0にする演算なので、このサンプルの場合、上の桁から

と演算され、1001となりました。

6:補数演算子について

補数演算子はビットを反転させる演算で、C#での演算子は『~』です。

以下のC#のサンプルプログラムをご覧ください。

実行結果は

になります。

~は1なら0、0なら1のように各桁のビットを反転させる機能があります。5桁目以上が反転して1になっていますが、4桁目までは1010となって0101が反転していることがわかります。

演算子の優先順位


ここまで紹介した演算子の優先順位はどうなっているのでしょうか。以下は優先度の高い順になっています。

補数演算子 ~
シフト演算子 <<と>>
論理AND演算子 &
論理排他的OR演算子 ^
論理OR演算子 |

例えば、以下の計算をすると

実行結果は

になります。

i2が先に左シフトされて0100になるので、その後に&でi1と掛け算をして0100となるわけです。

ビット演算によるフラグ管理とは


ここまで、C#でのビット演算とその優先順位について解説してきました。では、実際にC#でビット演算を使うのはどのような場合でしょうか。その多くが『ビットフラグ』と呼ばれるフラグの操作や管理をする場合です。

『ビットフラグ』とはTrue or Falseのみを扱いたい情報が複数個あるときに、いくつかのフラグの情報をひとまとめにして、変数に持たせたもののことです。

ビットフラグのメリットは?

例えば、家の電灯のON/OFF情報を管理するシステムをイメージしてください。ビットフラグを使わずbool型でフラグ管理をする場合、以下のように沢山の変数が必要になります。

これをビットフラグで表すと

だけで良いのです。

ビットフラグのメリットは、1つの変数で、桁の数だけのフラグを使えるということです。

それだけではありません。例えば、上記のシステムに『家中の電灯を一度に消す機能』を追加した場合、ビットフラグを使わない場合は、フラグを1つずつOFFにしなければなりません。しかしビットフラグであれば、一度に全てOFFにすることができます。

さらに、家に子供部屋を追加した場合、ビットフラグを使わない場合は、新規に

を追加するので、プログラム全体に修正が入ることになります。『家中の電灯を一度に消す機能』の修正が漏れてしまうことも考えられます。

ビットフラグにしておけば『家中の電灯を一度に消す機能』の修正は必要ありません。このように、機能追加があった際に、修正箇所をグッと減らすこともできるのです。

ビット演算によるフラグ管理例

ビット演算の方法がわかったところで、フラグを一括で管理する方法について解説します。

1つの変数で複数のフラグを管理する方法は、ビット単位で考えます。例えば4ビットの数の1桁目が1の時はAの状態がTrueであるというようになります。

例:
フラグが101の時
1桁目が1なので、状態Aはtrue
2桁目は0なので、状態Bはfalse
3桁目は1なので、状態Cはtrue

上記のような考え方です。これをコード上でチェックするには、論理積を使って確認します。実際の例を見てみましょう。

実行結果: このように、確認したい桁だけ1として他の桁を0にした値(Aなら001)とフラグを論理積にかけると、調べたい桁が1かどうかを調べることができます。

また、調べたい桁が0だと、すべての桁が0になるので『(status & FLAG_A) != 0』という書き方でフラグの真偽が確認できます。

なお、確認したい桁だけ1として他の桁を0にした値をマスクビットと言い、このマスクビットと比較して値を取り出すことを「マスクする」と言うこともあります。確認したい桁だけ調べて他の値は必ず0にして目を向けない、必要な桁以外は覆い隠す、という意味から「マスク」と言われています。

ここで、もう1つフラグ管理の例を挙げてみます。上記の例のようにフラグ管理をするとき「AとBのどちらかがTrueの時」という判定をしたい場合も出てきます。こういう場合は、論理和が使えます。

実行結果:
SE
複数・シフト・論理AND・論理排他的OR・論理OR演算子の優先順位とビット演算の実用性についてわかりました。
PM
実際にコードを書いて覚えていきましょう。

C#におけるビット演算のやり方を覚えよう

C#のビット演算の方法や、ビットフラグのメリットについてご紹介しました。

C#での開発を行うにあたって、ビット演算は必須スキルではないものの知っていると非常に便利であることがご理解頂けましたでしょうか。

「2進数」や「ビット」と聞いただけで『よくわからない!』と毛嫌いしてしまうのは非常にもったいないのでぜひ当サイトを熟読して、C#のビット演算のやり方を身に付けてください。


.NET分野でのキャリアアップをお考えの方は、現在募集中の求人情報をご覧ください。

また、直接のエントリーも受け付けております。

エントリー(応募フォーム)

Search

Popular

reccomended

Categories

Tags