Javaプログラミング ビット操作¶
はじめに¶
アプリケーションプログラムでは通常プログラミング言語が用意したデータ型を使って処理したいデータをマッピングします。
しかし、ハードウェアに近い処理やネットワーク処理などでは、ビット単位でデータを扱うことが必要になります。
そこで、プログラミング言語が用意したデータ型(通常整数型、Javaならbyte, short, int, longなど)を使って、そのデータの中の任意の位置のbitを読み書きする操作をプログラミングすることになります。
ビット操作事例¶
基礎事項¶
ビットマスクの作り方¶
あるデータサイズ(bit長)のデータの中の一部分のbitを読み書きする際に、ビットマスクを使用します。
ビットマスクは、データサイズ(bit長)のデータのうち読み書き対象ビット位置を1に、それ以外を0とした2進数で表現します。
例えば、8bit長のデータにおいて、MSB側から4~6bit目(下図のようにLSBから0オリジンでインデックスを付与した場合、4-2bit目)の3bitを読み書きする場合に使用するビットマスクは次のようになります。
MSB 7 6 5 4 3 2 1 0 LSB +-----+-----+---+ | | | | +-----+-----+---+ |<--->| ビットマスク 0 0 0 1 1 1 0 0
このビットマスクの作成方法は次のようになります。
- データサイズと同じサイズ(bit長)で全てのビットが1のデータを用意
1 1 1 1 1 1 1 1
- 読み書きするbit数と同じ数だけ左シフト
この例では3bitを読み書きするので左に3bitシフト1 1 1 1 1 0 0 0
- ビットを反転
0 0 0 0 0 1 1 1
- 読み書きするビット位置へ左シフト
この例では左に2bitシフト0 0 0 1 1 1 0 0
読み書きするビット位置への左シフト量は、次の計算式を使います。
左シフト量 = データサイズ - (MSBから数えた読み書き開始bit位置 + 1) - 読み書きbit数 この例では、 8 - (4 - 1) - 3 = 2
ビットマスクを使用したデータ読み書き方法¶
例)8bit のデータの中から3bit(符号なし整数)をビットマスクを使って読み出す方法¶
8bitデータ 01011010 ビット毎の&演算 & ビットマスク 00011100 ----------------------------------------- &演算結果 00011000 (1) (1)を2ビット右シフト 00000110 注) シフト量は読み出しビット位置により異なる。 ^^^ シフト演算子は>>>を使用する。 読み出し結果
例)8bit のデータの中から3bit(符号付き整数)をビットマスクを使って読み出す方法¶
8bitデータ 01011010 ビット毎の&演算 & ビットマスク 00011100 ----------------------------------------- &演算結果 00011000 (1) ^ (1)にて読み出しビット最上位が 1かを判定する 読み出しビット最上位が0の場合 (1)を2ビット右シフト 00000110 注) シフト量は読み出しビット位置により異なる。 読み出しビット最上位が1の場合 ビットマスクの反転を作成し、 11100011 (1)とビット毎の|を取る | 00011000 ----------------------------------------- |演算結果 11111011 (2) (2)を2ビット右シフト 11111110 注) シフト量は読み出しビット位置により異なる。
例)8bit のデータの中3bitをビットマスクを使って書き込む方法¶
8bitデータ 01011010 ビット毎の&演算 & ビットマスクを反転 11100011 ----------------------------------------- &演算結果 01000010 (1) 書き込みたいデータ(3bit) 00000101 (2) (2)を2ビット左シフト 00010100 (3) (1) データ 01000010 ビット毎の|演算 | (3) データ 00010100 ----------------------------------------- |演算結果 01010110 ^^^
特定のビット(1個)を読み書きする(ビッグエンディアン)¶
次のような32bit幅のデータに対して、指定したビット位置のビットを読み書きします。エンディアンはビッグエンディアンとします。
MSB 31 24 23 16 15 8 7 0 LSB +--------+--------+--------+--------+ |10011010|10111100|11011110|11110000| +--------+--------+--------+--------+
32bit幅のデータで任意の1箇所のビットが0か1かを判別する¶
14bitが0か1かを判別するコード例を示します。
int data = 0b10011010_10111100_11011110_11110000;
int mask = 1 << 14;
if ((data & mask) != 0) {
// 14bitは1
} else {
// 14bitは0
}
32bit幅のデータで任意の1箇所のビットに値をセットする¶
14bitにビットをセットするコード例を示します。
int data = 0b10011010_10111100_11011110_11110000;
int writeBit = 1 << 14;
data |= writeBits;
14bitにビットをクリアするコード例を示します。
int data = 0b10011010_10111100_11011110_11110000;
int writeBit = ~(1 << 14);
data &= writeBits;
特定のビット(複数)を読み書きする(ビッグエンディアン)¶
次のような32bit幅のデータに対して、ビット位置とビット数を指定してデータを読み書きします。ただし、エンディアンはビッグエンディアンとします。
MSB 31 24 23 16 15 8 7 0 LSB +--------+--------+--------+--------+ |10011010|10111100|11011110|11110000| +--------+--------+--------+--------+
32bit幅のデータから23bit~20bitの4bit幅のデータを符合なし整数値として読み出す¶
MSB 31 24 23 16 15 8 7 0 LSB +--------+--------+--------+--------+ |********|1011****|********|********| +--------+--------+--------+--------+
- まず、23bit~20bitの4bitが1、その他が0の32bit値(ビットマスク)000000001111000000000000を作成します。
int mask = ~(-1 << 4) << 20;
- 32bitデータと作成したビットマスクとのビット毎のANDを取ります。
int data = 0b10011010_10111100_11011110_11110000; int mask = ~(-1 << 4) << 20; int masked = data & mask;
- その結果を右20bitシフトします。
int data = 0b10011010_10111100_11011110_11110000; int mask = ~(-1 << 4) << 20; int masked = data & mask; int result = masked >> 20;
32bit幅のデータから23bit~20bitの4bit幅のデータを符合付整数値として読み出す¶
MSB 31 24 23 16 15 8 7 0 LSB +--------+--------+--------+--------+ |********|1011****|********|********| +--------+--------+--------+--------+
- まず、23bit~20bitの4bitが1、その他が0の32bit値(ビットマスク)000000001111000000000000を作成します。
int mask = ~(-1 << 4) << 20;
- 32bitデータと作成したビットマスクとのビット毎のANDを取ります。
int data = 0b10011010_10111100_11011110_11110000; int masked = data & mask;
- 読み出しビット幅の最上位ビットが1かを判定(符号ビット)
if (masked & Integer.highestOneBit(mask)) != 0) { // 読み出しビット幅の最上位ビットが1の場合の処理 }
- 読み出しビット幅の最上位ビットが1の場合、そのビットよりMSB側のビットを1にする
if (masked & Integer.highestOneBit(mask)) != 0) { masked |= ~mask; }
- その結果を右20bitシフトします。
int result = masked >> 20;
以上の処理をまとめると次のコードとなります。
int data = 0b10011010_10111100_11011110_11110000;
int mask = ~(-1 << 4) << 20;
int masked = data & mask;
if (masked & Integer.highestOneBit(mask)) != 0) {
masked |= ~mask;
}
int result = masked >> 20;
32bit幅のデータから23bit~20bitの4bit幅のデータに整数値を書き込む¶
- まず、23bit~20bitの4bitが1、その他が0の32bit値(ビットマスク)000000001111000000000000を作成します。
int mask = ~(-1 << 4) << 20;
- ビットマスクをビット反転したビットクリア値 111111110000111111111111を作成します。
int clear = ~mask;
- 32bitデータと作成したビットクリアとのビット毎のANDを取ります。
int data = 0b10011010_10111100_11011110_11110000; int cleared = data & clear;
- 書き込みデータを左へ20bitシフトし、ビットマスクのビット毎のANDを取ります。
int write = 0b1010; int writeBits = write << 20 & mask;
- 32bitデータとビットクリアとのビット毎のAND結果と、この結果をビット毎のOR取りします。
int result = cleared | writeBits;
ビット操作API¶
Java SE 標準API¶
IntegerおよびLongクラスのビット操作メソッド¶
- bitCount - 指定した値の2の補数2進数表現で1が立っているビット数を数える
- highestOneBit - 指定した値の最上位で1が立つビットと同じ位置のビットを1とし残りは0とした値を返す
- lowestOneBit - 指定した値の最下位で1が立つビットと同じ位置のビットを1とし残りは0とした値を返す
- numberOfLeadingZeros - 指定した値の最上位で1が立つビットより上位にある0のビット数を数える
- numberOfTrailingZeros - 指定した値の最下位で1が立つビットより下位にある0のビット数を数える
- reverse - 指定した値のビット並びの逆順
- rotateLeft - 指定した値のビット並びを左に指定した数だけローテートする、左側を飛び出たビットは右端(最下位)ビットになる
- rotateRight - 指定した値のビット並びを右に指定した数だけローテートする、右側を飛び出たビットは左端(最上位)ビットになる