ビット演算子/シフト演算子

JavaScript で用意されている演算子の中で、整数の値に対してビット単位で処理を行うために用意されているビット演算子の種類と使い方について解説します。

(Last modified: )

ビット演算子の種類と使い方

ビット演算子には 7 種類が用意されています。

&   ビット論理積(AND)
|   ビット論理和(OR)
^   ビット排他的論理和(XOR)
~   ビット論理否定(NOT)
<<  左シフト
>>  符号あり右シフト
>>> 符号なし右シフト

JavaScript では内部的に数値はすべて 64 ビットの浮動小数点数として処理されます。ただ、ビット演算子は 32 ビットの整数に変換してから処理されます。その為、小数部分は切り捨てられ、また 32 ビットを超える部分も切り捨てられますのでご注意ください。

ビット演算では数値を 2 進数の形式で表示し、各ビットに対して演算を行います。例えば 10 進数の 27543 を 2 進数で表すと次のようになります。

10進数 : 27543
 2進数 : 0b110101110010111

0000 0000 0000 0000 0110 1011 1001 0111

それではビット演算子の中で論理積、論理和、排他的論理和、論理否定についてそれぞれ確認していきます。シフト演算子については後半解説します。

ビット論理積(AND)

ビット論理積(&)は演算子の前と後ろのオペランドの同じ位置にあるビットを比較し、両方のビットが共に 1 の場合だけ 1 にします。それ以外は 0 にします。次のサンプルをみてください。

let num1 = 27543;
let num2 = 19024;

console.log(num1 & num2);
>> 18960

数値 27543 と 19024 のビット論理積を行った結果をコンソールに出力しています。ビット単位で 2 つの数値を比較し、同じ位置のビットで両方とも 1 の場合だけ 1 としています。

0000 0000 0000 0000 0110 1011 1001 0111 = 27543
0000 0000 0000 0000 0100 1010 0101 0000 = 19024
---------------------------------------
0000 0000 0000 0000 0100 1010 0001 0000 = 18960

結果としてコンソールには 0b100101000010000 ( 10 進数で 18960 ) が出力されました。

ビット論理和(OR)

ビット論理和(|)は演算子の前と後ろのオペランドの同じ位置にあるビットを比較し、少なくともどちらか一つのビットが 1 の場合に 1 にします。それ以外は 0 にします。次のサンプルをみてください。

let num1 = 27543;
let num2 = 19024;

console.log(num1 | num2);
>> 27607

数値 27543 と 19024 のビット論理和を行った結果をコンソールに出力しています。ビット単位で 2 つの数値を比較し、同じ位置のビットで少なくとも一つが 1 の場合は 1 としています。

0000 0000 0000 0000 0110 1011 1001 0111 = 27543
0000 0000 0000 0000 0100 1010 0101 0000 = 19024
---------------------------------------
0000 0000 0000 0000 0110 1011 1101 0111 = 27607

結果としてコンソールには 0b110101111010111 ( 10 進数で 27607 ) が出力されました。

ビット排他的論理和(XOR)

ビット排他的論理和(^)は演算子の前と後ろのオペランドの同じ位置にあるビットを比較し、どちらか一つのビットだけが 1 の場合に 1 にします。それ以外は 0 にします。次のサンプルをみてください。

let num1 = 27543;
let num2 = 19024;

console.log(num1 ^ num2);
>> 8647

数値 27543 と 19024 のビット排他的論理和を行った結果をコンソールに出力しています。ビット単位で 2 つの数値を比較し、同じ位置のビットでどちらか一つだけが 1 の場合は 1 としています。

0000 0000 0000 0000 0110 1011 1001 0111 = 27543
0000 0000 0000 0000 0100 1010 0101 0000 = 19024
---------------------------------------
0000 0000 0000 0000 0010 0001 1100 0111 = 8647

結果としてコンソールには 0b10000111000111 ( 10 進数で 8647 ) が出力されました。

ビット論理否定(NOT)

ビット論理否定(~)は演算子の後ろのオペランドに対してビットが 1 の場合は 0 、 0 の場合は 1 にします。次のサンプルをみてください。

let num1 = 27543;

console.log(~num1);
>> -27544

数値 27543 のビット論理否定を行った結果をコンソールに出力しています。ビットが 1 の場合は 0 、 0 の場合は 1 とします。

0000 0000 0000 0000 0110 1011 1001 0111 = 27543
---------------------------------------
1111 1111 1111 1111 1001 0100 0110 1000 = -27544

結果としてコンソールには 0b11111111111111111001010001101000 ( 10 進数で -27544 ) が出力されました。

JavaScript では負の値を 2 の補数表現を使って表します。最上位ビットは符号ビットとなり、最上位ビットが 1 の場合は負の値となります。

シフト演算子の種類と使い方

ビット演算子の中でも次の 3 つはシフト演算子とも呼ばれます。

<<  左シフト
>>  符号あり右シフト
>>> 符号なし右シフト

シフト演算子は演算子の前のオペランドを後のオペランド分だけ左または右へシフトします。

それでは左シフト、右シフト(符号あり)、右シフト(符号なし)についてそれぞれ確認していきます。

左シフト

左シフト(<<)は演算子の前のオペランドを後のオペランド分だけ左へシフトします。 32 ビットを超えた分は破棄されます。シフトしたことにより空いた右端のビットには 0 が入ります。次のサンプルをみてください。

let num = 27543;

console.log(num << 3);
>> 220344

数値 27543 を 3 ビット分左へシフトした結果をコンソールに出力しています。

0000 0000 0000 0000 0110 1011 1001 0111 = 27543
---------------------------------------
0000 0000 0000 0000 1101 0111 0010 1110  // 1 ビット分左へシフト
0000 0000 0000 0001 1010 1110 0101 1100  // 2 ビット分左へシフト
0000 0000 0000 0011 0101 1100 1011 1000  // 3 ビット分左へシフト

結果としてコンソールには 0b110101110010111000 ( 10 進数で 220344 ) が出力されました。

符号あり右シフト

符号あり右シフト(>>)は演算子の前のオペランドを後のオペランド分だけ右へシフトします。右端からはみ出た分は破棄されます。シフトしたことにより空いた左端のビットには最上位ビット が 0 だった場合はシフトしたあとも 0 、最上位ビット が 1 だった場合はシフトしたあとも 1 が入ります。結果として右シフトしても符号は変わりません。

次のサンプルをみてください。最初は最上位ビットが 0 の場合です。

let num = 27543;

console.log(num >> 3);
>> 3442

数値 27543 を 3 ビット分右へシフトした結果をコンソールに出力しています。

0000 0000 0000 0000 0110 1011 1001 0111 = 27543
---------------------------------------
0000 0000 0000 0000 0011 0101 1100 1011  // 1 ビット分右へシフト
0000 0000 0000 0000 0001 1010 1110 0101  // 2 ビット分右へシフト
0000 0000 0000 0000 0000 1101 0111 0010  // 3 ビット分右へシフト

最上位ビットが 0 でしたので、右へシフトしたあとの最上位ビットには 0 が入ります。結果としてコンソールには 0b110101110010 ( 10 進数で 3442 ) が出力されました。

次は最上位ビットが 1 の場合です。

let num = -27544;

console.log(num >> 3);
>> -3443

数値 -27544 を 3 ビット分右へシフトした結果をコンソールに出力しています。

1111 1111 1111 1111 1001 0100 0110 1000 = -27544
---------------------------------------
1111 1111 1111 1111 1100 1010 0011 0100  // 1 ビット分右へシフト
1111 1111 1111 1111 1110 0101 0001 1010  // 2 ビット分右へシフト
1111 1111 1111 1111 1111 0010 1000 1101  // 3 ビット分右へシフト

最上位ビットが 1 でしたので、右へシフトしたあとの最上位ビットには 1 が入ります。結果としてコンソールには 0b11111111111111111111001010001101 ( 10 進数で -3443 ) が出力されました。

符号なし右シフト

符号なし右シフト(>>>)は演算子の前のオペランドを後のオペランド分だけ右へシフトします。右端からはみ出た分は破棄されます。シフトしたことにより空いた左端のビットには 0 が入ります。結果として元の値が負の値だった場合、右シフトすると正の値となります。

次のサンプルをみてください。最初は最上位ビットが 0 の場合です。

let num = 27543;

console.log(num >>> 3);
>> 3442

数値 27543 を 3 ビット分右へシフトした結果をコンソールに出力しています。

0000 0000 0000 0000 0110 1011 1001 0111 = 27543
---------------------------------------
0000 0000 0000 0000 0011 0101 1100 1011  // 1 ビット分右へシフト
0000 0000 0000 0000 0001 1010 1110 0101  // 2 ビット分右へシフト
0000 0000 0000 0000 0000 1101 0111 0010  // 3 ビット分右へシフト

元の値が正の値だった場合は符号あり右シフトの場合と同じ結果になります。結果としてコンソールには 0b110101110010 ( 10 進数で 3442 ) が出力されました。

次は最上位ビットが 1 の場合です。

let num = -27544;

console.log(num >>> 3);
>> 536867469

数値 -27544 を 3 ビット分右へシフトした結果をコンソールに出力しています。

1111 1111 1111 1111 1001 0100 0110 1000 = -27544
---------------------------------------
0111 1111 1111 1111 1100 1010 0011 0100  // 1 ビット分右へシフト
0011 1111 1111 1111 1110 0101 0001 1010  // 2 ビット分右へシフト
0001 1111 1111 1111 1111 0010 1000 1101  // 3 ビット分右へシフト

最上位ビットには 0 が入りますので元の値は負の値でしたがシフとした結果は正の値となります。結果としてコンソールには 0b11111111111111111001010001101 ( 10 進数で 536867469 ) が出力されました。

-- --

JavaScript で用意されている演算子の中で、整数の値に対してビット単位で処理を行うために用意されているビット演算子の種類と使い方について解説しました。

( Written by Tatsuo Ikura )

Profile
profile_img

著者 / TATSUO IKURA

プログラミングや開発環境構築の解説サイトを運営しています。