ビット演算は、昔はメモリ領域の節約のために用いられる手法でしたが、最近ではメモリも潤沢にあるので、使う機会も減りました。
ですが、使いどころによっては様々な状態を表すフラグ管理として有用となりますので、いつでも使えるようにしておくことが望ましい機能です。
本記事では、Javaにおけるビット演算について解説します。
記事内に記載しているプログラムは、Java11を使って動作確認をしています。
Javaのビット演算
Javaのビット演算について、以下の内容を解説します。
- Javaでのビット演算の扱い方
- AND演算
- OR演算
- XOR演算
- NOT演算
- 左シフト演算
- 右シフト演算
Javaでのビット演算の扱い方
ビット演算はその名の通り、1ビット単位で演算を行うので、プログラムも2進数で表現していきます。
Javaで2進数で表現するには、先頭に 0b をつけます。
int型の変数として扱うと、内部的には32ビットで扱われます。
下記のプログラムでは、下位8ビットの定義しています。
int n = 0b00001010; System.out.println(n); // -> 10 System.out.println(Integer.toBinaryString(n)); // -> 1010
そのまま出力すると10進数表記となります。
2進数表記とするには、Integer.toBinaryStringを使います。(先頭の0になる部分は省略されます)
byte型の変数として扱うと、内部的には8ビットで扱われます。
byte b = 0b00000110; System.out.println(b); // -> 6 System.out.println(Integer.toBinaryString(b)); // -> 110
AND演算
AND演算を行うには、演算子&を使います。
AND演算は演算するビット同士が1であれば結果は1、それ以外の組み合わせは0となる計算を行います。
int result = n & b; System.out.println(result); // -> 2 System.out.println(Integer.toBinaryString(n)); // -> 10
上記のプログラムは、「0b1010」と「0b0110」のAND演算になるので、結果は「0b0010」となって2となります。
OR演算
OR演算を行うには、演算子|を使います。
OR演算は演算するどちらかのビット1であれば結果は1、両方のビットが0であれば結果は0となる計算を行います。
result = n | b; System.out.println(result); // -> 14 System.out.println(Integer.toBinaryString(result)); // -> 1110
上記のプログラムは、「0b1010」と「0b0110」のOR演算になるので、結果は「0b1110」となって14となります。
XOR演算
XOR演算を行うには、演算子^を使います。
XOR演算は演算するビットが0と1で異なる場合は結果は1、ビットが同じ0同士または1同士であれば結果は0となる計算を行います。
result = n ^ b; System.out.println(result); // -> 12 System.out.println(Integer.toBinaryString(result)); // -> 1100
上記のプログラムは、「0b1010」と「0b0110」のXOR演算になるので、結果は「0b1100」となって14となります。
NOT演算
NOT演算を行うには、演算子~を使います。
NOT演算は1つ値の持つ各ビットを反転させる計算を行います。
result = ~n; System.out.println(result); // -> -11 System.out.println(Integer.toBinaryString(result)); // -> 11111111111111111111111111110101
上記のプログラムは、「0b1010」の各ビットを反転させて1を加えた補数表現となります。
左シフト演算
左シフト演算は、演算子<<を使って、ビットをそのまま指定した数だけ左に移動させます。
左端はそのまま桁あふれとなり、右端から0が入ってきます。
result = n << 2; System.out.println(result); // -> 40 System.out.println(Integer.toBinaryString(result)); // -> 101000
上記のプログラムは、「0b1010」を2桁左に移動して、「0b101000」となって40となります。
右シフト演算(符号あり)
右シフト演算は、演算子>>を使って、ビットをそのまま指定した数だけ右に移動させます。
右端はそのまま桁あふれとなり、左端は正の数であれば0、負の数であれば1が入って、符号をそのまま引継ぎます。
result = n >> 2; System.out.println(result); // -> 2 System.out.println(Integer.toBinaryString(result)); // -> 10
上記のプログラムは、「0b1010」を2桁右に移動して、「0b0010」となって2となります。
負の数に対して、>>による右シフト演算を行うと、負数のまま右シフトとした補数表現となります。
右シフト演算(符号なし)
符号なし右シフト演算は、演算子>>>を使って、ビットをそのまま指定した数だけ右に移動させます。
右端はそのまま切り捨てられ、左端は必ず0が入ります。
result = n >>> 2; System.out.println(result); // 2 System.out.println(Integer.toBinaryString(result)); // -> 10
上記のプログラムは、「0b1010」を2桁右に移動して、「0b0010」となって2となります。
負の数に対して、>>>による右シフト演算を行うと、符号部分が0となるため正の数となります。
まとめ
Javaのビット演算についてまとめると、以下となります。
- Javaで2進数を扱うには、値の先頭に0bを付ける。
- ビット演算は各ビットに対して演算を行う。
- シフト演算は各ビットを移動させて演算を行う。
通常あまり使う機会はないかもしれませんが、桁に意味を持たせてフラグ管理したい場合などに使ってみてはいかがでしょうか。
今回は、Javaのビット演算について解説しました。
以上、参考になれば幸いです。
コメント