読者です 読者をやめる 読者になる 読者になる

ビット演算の基礎

ビット演算を使用すると、1つの整数型データをビット単位で区切って、複数の用途に割り当てることができます。 例えば、符号なし整数 (unsigned int) のデータ長が32ビットの環境であれば、一つの変数で32個の真理値 (bool: true/false に相当) を格納することができます。 また、これらの真理値を複数個まとめて参照・操作することも可能です。

しかし最近のプログラミング環境では、潤沢なメモリやライブラリの普及などにより、ビット演算を利用するような場面は、以前と比べて少なくなっています。 そのため、ある程度長くプログラミングを学んでいるにも関わらず、ビット演算が使えない、あるいはその概念自体を知らないという人も見かけるようになりました。

というわけで、今回はビット演算の基本中の基本である、ビット値の参照・操作の方法とその手順について解説します。 ちなみに、当エントリの説明では図のスペース節約のため、8ビット符号なし整数 (BYTE: unsigned char) を操作の対象としていますので、実際に使用する場合は適当なデータ長に置き換えて考えてください。

ビット値の取得

ある整数値を二進表記したとき、その特定のビットの値 (0 または 1) を取得する関数はどのように書けば良いでしょうか。 例えば、3ビット目の値を取得するプログラムは以下のようになります。 (注: 一番右端のビットは「0ビット目」と数えます。)

//3ビット目の値を取得
int getBit3(BYTE by){
    return (0x8 & by) ? 1 : 0;
}

この関数は、0x8 & by の値が 0 の場合は 0 を、それ以外の場合は 1 を返します。
仮に、by の値が 100 だとすると、戻り値は 0 になります。

by の値が 200 ならば、戻り値は 1 となります。

つまり、2進表記をしたときに、4桁目 (=3ビット目) だけが 1 であるような数 (= 0x8) との AND を計算すればよいわけです。 このような、ビット演算のために用意される値をマスクと呼びます。

より一般的に、n 番目のビットの値を取得する関数は次のようになります。

//nビット目の値を取得
int getBit(int n, BYTE by){
    assert(0 <= n && n < 8);
    return ((0x1 << n) & by) ? 1 : 0;
}

左シフト演算 << を用いて、n ビット目だけが 0 となるようなマスク値を作成し、これと by の AND (&) を計算すればOKです。

ビット値の設定

まず、特定のビットを立てる (1にする) 操作を考えます。 前節でビット値を調べるために使用したマスクと同じもの、即ち、立てたい位置のビットだけが 1 であるような値を用意し、入力値との OR を取ります。

//nビット目を立てる
int turnBitOn(int n, BYTE by){
    assert(0 <= n && n < 8);
    return (0x1 << n) | ny;
}

当たり前ですが、最初から立っているビットを立てようとした場合は、何も変化は起こらず、入力値がそのまま出力値となります。

次は、ビットを倒す (0にする) 操作を考えましょう。 この操作を行うにはまず、倒したい位置のビットだけが 0 であるようなマスク値を用意します。 そのようなマスク値は、ビットを立てるときに使用したマスク値に対して (NOT 演算を適用することで得られます。 (! 演算子による論理NOTではなく、~ 演算によるビットNOTを用いることに注意してください。)

こうして得られたマスク値と入力値の AND を取れば、お目当てのビットを倒すことができます。

//nビット目を倒す
int turnBitOff(int n, BYTE by){
    assert(0 <= n && n < 16);
    return ~(0x1 << n) & by;
}

もちろんビットを立てる操作と同様、予め倒れているビットを更に倒そうとしても、入力値は変化せずにそのまま出力値となります。

成田