【C#入門】何のためにビット演算をするの?必要性とやり方を紹介

知っておけば超便利! ビット演算を解説


ビット演算ってなに? どう使ってなんの役に立つの?

ビット演算でフラグ管理がしたい! マスクビットって何?

情報処理を勉強している方でしたら、基礎的な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

まとめ

今回の記事では、以下の点について解説致しました。

  • ビット演算とは
  • ビット演算の方法
  • ビット演算を使ったフラグの一括管理方法
  • マスクビットについて

ビット演算は一見分かりにくいですが、理解しておくと便利です。

ぜひここで使い方の基礎を覚えて、活用していってください!

この記事を書いた人

1991年生まれ。双子座。
理系大学で認証システムを学んだ後、アプリ開発者となる。
新しく学ぶ人に寄り添った記事を心がけて執筆します。
芸術が好き。いつか猫と暮らすのが夢。

目次