ビット演算ってなに? どう使ってなんの役に立つの?
ビット演算でフラグ管理がしたい! マスクビットって何?
情報処理を勉強している方でしたら、基礎的な2進数についてある程度の知識はあるかと思います。
しかし実際にC#のコーディングで、どのようにビット演算を使うかご存知でしょうか?
こんにちは、エンジニアでC#ライターの遠藤です!
今回の記事は以下のような方へ向けて書きました。
- ビット演算について知りたい方
- ビット演算の使い道について知りたい方
- ビット演算でフラグ処理をしたいと考えている方
この記事を読んでいただければ、ビット演算の使い道について理解することができます。ぜひ最後までお付き合いください!
ビット演算とは
まずはじめに、ビット演算とはある数を2進数で計算する処理の事を言います。例えば、「7 AND 5」 をビット演算すると「0b111 AND 0b101 -> 0b101」となります。
細かい計算方法は後ほど解説しますが、 このように2進数の計算をビット演算と言います。
続けて、ビット演算の必要性から具体的な使い方、実際の使いどころについて解説します。
なぜビット演算をする必要があるのか
ビット演算を使用するよくあるケースでは、一つの変数で複数のフラグを管理する場合です。もちろんboolean型の変数を沢山使用しても良いのですが、それだと複雑になってしまったり、メモリを多く使ってしまう事になります。
こういった時にフラグを一括で管理して、ビット演算でフラグ確認を行うという方法をとることで、メモリ節約などができます。
ビット演算のやり方
それでは具体的にC#でビット演算をする方法について解説致します。
前提として、2進数表記の数値の表し方は「0b」が頭に付き、その後に2進数の数が付く形となります。例として、7は「0b111」です。
論理積
論理積は、演算子で言うと「&」です。「A&B」は「AかつB」という風にも言えますね。
これをC#でビット演算すると、以下のようになります。
using System;
class Program
{
public static void Main()
{
int a = 7 & 5; // 111 AND 101 -> 101(=5)
Console.WriteLine(a);
}
}
実行結果:
5
論理積ではそれぞれの桁を比較して、どちらも1の時だけ演算結果のその桁が1になります。
111(=7)と101(=5)では、真ん中の桁だけ0があるので、結果は101(=5)となります。
論理和
論理和は、演算子で言うと「|」です。「A|B」は「AまたはB」と言えます。
これをC#で表すと、以下になります。
using System;
class Program
{
public static void Main()
{
int a = 7 | 5; // 111 OR 101 -> 111(=7)
Console.WriteLine(a);
}
}
実行結果:
7
論理和ではそれぞれの桁を比較して、どちらか一方が1の時に演算結果のその桁が1になります。つまり、111(=7)と101(=5)では、真ん中の桁も片方が1なので結果も1となり、111(=7)となります。
排他的論理和
排他的論理和は、演算子で表すと「^」です。残念ながら「A^B」は言葉では表しにくいです……。
排他的論理和がどういった演算をするかというと、それぞれの桁を比較して、値が一致しない場合に演算結果が1となります。
実際の例を見てみましょう。
using System;
class Program
{
public static void Main()
{
int a = 7 ^ 5; // 111 XOR 101 -> 010(=2)
Console.WriteLine(a);
}
}
実行結果:
2
これは111(=7)と101(=5)を比較して各桁の値が異なる場合に1となるので、値の異なる真ん中の桁だけが1となり、結果が010(=2)となります。
反転
ビット演算の中に、「~」という演算子が使われるケースがあります。これは、ビットの値を反転させることを表しています。
具体的な例は以下のようになります。
using System;
class Program
{
public static void Main()
{
byte a = 5;
Console.WriteLine((byte)~a); // 0000 0101を反転 -> 1111 1010(=250)
}
}
実行結果:
250
ビット演算でフラグを管理する方法
ビット演算の方法が分かったところで、フラグを一括で管理する方法について解説致します。
一つの変数で複数のフラグを管理する方法は、ビット単位で考えます。例えば4ビットの数の一桁目が1の時はAの状態がTrueであるというようになります。
例:
フラグが101の時 1桁目が1なので、状態Aはtrue 2桁目は0なので、状態Bはfalse 3桁目は1なので、状態Cはtrue
上記のような考え方です。これをコード上でチェックするには、論理積を使って確認します。
実際の例を見てみましょう。
using System;
class Program
{
//説明の便宜上定数を取ります
const int FLAG_A = 1; // 001(=1)
const int FLAG_B = 2; // 010(=2)
const int FLAG_C = 4; // 100(=4)
public static void Main()
{
byte status = 5; // 101(=5)
Console.WriteLine("Aの状態は" + ((status & FLAG_A) != 0) + "です。");
Console.WriteLine("Bの状態は" + ((status & FLAG_B) != 0) + "です。");
Console.WriteLine("Cの状態は" + ((status & FLAG_C) != 0) + "です。");
}
}
実行結果:
Aの状態はTrueです。 Bの状態はFalseです。 Cの状態はTrueです。
このように、確認したい桁だけ1として他の桁を0にした値(Aなら001)とフラグとを論理積にかけると、調べたい桁が1かどうかを調べることができます。
また、調べたい桁が0だと、すべての桁が0になるので、「(status & FLAG_A) != 0」という書き方でフラグの真偽が確認できます。
なお、確認したい桁だけ1として他の桁を0にした値をマスクビットと言い、このマスクビットと比較して値を取り出すことを「マスクする」と言ったりします。
確認したい桁だけ調べて、他の値は必ず0にして目を向けない。必要な桁以外は覆い隠すという意味から「マスク」と言われています。
ここで、もう一つフラグ管理の例を挙げてみます。
上記の例のようにフラグ管理をするとき、「AとBのどちらかがTrueの時」という判定をしたい場合も出てきます。こういう場合は、論理和が使えます。
using System;
class Program
{
const int FLAG_A = 1; // 001(=1)
const int FLAG_B = 2; // 010(=2)
const int FLAG_AB = FLAG_A | FLAG_B; // 001 OR 010 -> 011: 論理和を使ったフラグができる
public static void Main()
{
byte status = 5; // 101(=5)
Console.WriteLine("A, BどちらかはTrue? : " + ((status & FLAG_AB) != 0));
}
}
実行結果:
A, BどちらかはTrue? : True
まとめ
今回の記事では、以下の点について解説致しました。
- ビット演算とは
- ビット演算の方法
- ビット演算を使ったフラグの一括管理方法
- マスクビットについて
ビット演算は一見分かりにくいですが、理解しておくと便利です。
ぜひここで使い方の基礎を覚えて、活用していってください!