ビット演算子ではそのオペランドを 10 進数や 16 進数や 8 進数の数値ではなく、32 ビットの集合(0 と 1)として 扱います。例えば、10 進数の 9 の 2 進表現は 1001 です。ビット演算子はこのように 2 進表現にした上で演算を行いますが、標準の JavaScript の数値を返します。
次の表で JavaScript のビット演算子について説明します:
| 演算子 | 使用方法 | 説明 |
|---|---|---|
| ビットごとの AND | a & b |
オペランドの対応するビットがともに 1 である各ビットについて 1 を返します。 |
| ビットごとの OR | a | b |
オペランドの対応するビットがどちらかまたはともに 1 である各ビットについて 1 を返します。 |
| ビットごとの XOR | a ^ b |
オペランドの対応するビットがどちらか一方のみ 1 である各ビットについて 1 を返します。 |
| ビットごとの NOT | ~ a |
オペランドの各ビットを反転します。 |
| 左シフト | a << b |
2 進表現の a を b (< 32) ビット分だけ左にシフトします。右から 0 を詰めます。 |
| 符号を維持した右シフト | a >> b |
2 進表現の a を b (< 32) ビット分だけ右にシフトします。溢れたビットは破棄します。 |
| 0 埋め右シフト | a >>> b |
2 進表現の a を b (< 32) ビット分だけ右にシフトします。溢れたビットは破棄し、左から 0 を詰めます。 |
符号付き 32 ビット整数値
すべてのビット演算でオペランドは符号付き 32 ビット演算に、ビッグエンディアンおよび 2 の補数形式で変換されます。ビッグエンディアン形式とは、32 ビットを水平方向に並べたとき最上位ビット (最大値のビット位置) が左端にある形式です。2 の補数形式とは、ある値に対する負数 (例えば 5 と -5) は、正数のビットをすべて反転 (正数の NOT ビット演算、1 の補数として知られています) して 1 を加えたものです。例えば以下は、整数値 314 (10 進数) を表しています:
00000000000000000000000100111010
以下は ~314、すなわち 314 の 1 の補数を表しています:
11111111111111111111111011000101
最後に、以下は -314、すなわち 314 の 2 の補数を表しています:
11111111111111111111111011000110
2 の補数では、左端のビットが 0 であれば数値は正数、1 であれば数値は負数であることを保証します。よって、そのビットは符号ビットと呼ばれます。
数値 0 は、すべてのビットが 0 で構成される整数です。
0 (10 進数) = 00000000000000000000000000000000 (2 進数)
数値 -1 は、すべてのビットが 1 で構成される整数です。
-1 (10 進数) = 11111111111111111111111111111111 (2 進数)
数値 -2147483648 (16 進表記: -0x80000000) は、最初 (左端) を除きすべてのビットが 0 で構成される整数です。
-2147483648 (10 進数) = 10000000000000000000000000000000 (2 進数)
数値 2147483647 (16 進表記: 0x7fffffff) は、最初 (左端) を除きすべてのビットが 1 で構成される整数です。
2147483647 (10 進数) = 01111111111111111111111111111111 (2 進数)
数値 -2147483648 および 2147483647 は、符号付き 32 ビット数値で表現できる最小および最大の整数です。
ビットごとの論理演算子
概念的にビットごとの論理演算子は、以下のように機能します:
- オペランドは 32 ビット整数に変換され、ビット列(0 と 1)として表現されます。
- 第 1 のオペランドの各ビットは第 2 のオペランドの対応するビットと対にされます。第 1 ビットと第 1 ビット、 第 2 ビットと第 2 ビット、というように対にされます。
- 演算子は各ビットのペアに適用され、結果はビットごとに組み立てられます。
& (ビットごとの AND)
AND 演算は、ビットの各組で実行します。a AND b は、a と b の両方が 1 である場合にのみ 1 を出力します。AND 演算の真理値表は以下のとおりです:
| a | b | a AND b |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
例:
9 (10 進数) = 00000000000000000000000000001001 (2 進数)
14 (10 進数) = 00000000000000000000000000001110 (2 進数)
--------------------------------
14 & 9 (10 進数) = 00000000000000000000000000001000 (2 進数) = 8 (10 進数)
任意の数値 x と 0 とのビットごとの AND 演算は、0 を出力します。
任意の数値 x と -1 とのビットごとの AND 演算は、x を出力します。
| (ビットごとの OR)
OR 演算は、ビットの各組で実行します。a OR b は、a か b のいずれかが 1 である場合に 1 を出力します。OR 演算の真理値表は以下のとおりです:
| a | b | a OR b |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
9 (10 進数) = 00000000000000000000000000001001 (2 進数)
14 (10 進数) = 00000000000000000000000000001110 (2 進数)
--------------------------------
14 | 9 (10 進数) = 00000000000000000000000000001111 (2 進数) = 15 (10 進数)
任意の数値 x と 0 とのビットごとの OR 演算は、x を出力します。
任意の数値 x と -1 とのビットごとの OR 演算は、-1 を出力します。
^ (ビットごとの XOR)
XOR 演算は、ビットの各組で実行します。a XOR b は、a と b が異なる場合に 1 を出力します。XOR 演算の真理値表は以下のとおりです:
| a | b | a XOR b |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
例:
9 (10 進数) = 00000000000000000000000000001001 (2 進数)
14 (10 進数) = 00000000000000000000000000001110 (2 進数)
--------------------------------
14 ^ 9 (10 進数) = 00000000000000000000000000000111 (2 進数) = 7 (10 進数)
任意の数値 x と 0 とのビットごとの XOR 演算は、x を出力します。
任意の数値 x と -1 とのビットごとの XOR 演算は、~x を出力します。
~ (ビットごとの NOT)
NOT 演算は、各ビットで実行します。NOT a は、a を反転した値 (1 の補数として知られています) を出力します。NOT 演算の真理値表は以下のとおりです:
| a | NOT a |
| 0 | 1 |
| 1 | 0 |
例:
9 (10 進数) = 00000000000000000000000000001001 (2 進数)
--------------------------------
~9 (10 進数) = 11111111111111111111111111110110 (2 進数) = -10 (10 進数)
任意の数値 x のビットごとの NOT 演算は、-(x + 1) を出力します。例えば、~5 で -6 を出力します。
ビットシフト演算子
ビットシフト演算子は 2 つのオペランドをとります。第 1 のオペランドはシフトされる数を指定し、第 2 のオペランドは、第 1 のオペランドをシフトさせるビット数を指定します。シフト演算の方向は使用する演算子によって決まります。
シフト演算子はそのオペランドをビッグエンディアン形式の 32 ビット整数に変換し、左のオペランドと同じ型の結果を返します。右のオペランドは 32 より小さくするべきであり、そうでない場合は下位 5 ビットのみが用いられます。
<< (左シフト)
この演算子は、第 1 オペランドを指定したビット数分だけ左にシフトします。左に溢れたビットは破棄されます。0 のビットを右から詰めます。
例えば、9 << 2 の結果は 36 になります。
9 (10 進数): 00000000000000000000000000001001 (2 進数)
--------------------------------
9 << 2 (10 進数): 00000000000000000000000000100100 (2 進数) = 36 (10 進数)
任意の数値 x を y ビット左にシフトすると、x * 2^y になります。
>> (符号を維持した右シフト)
この演算子は、第 1 オペランドを指定したビット数分だけ右にシフトします。右に溢れたビットは破棄されます。左端のビットのコピーを左から詰めます。新たな左端のビットは従来の左端のビットと同じであることから、符号ビット (左端のビット) は変わりません。それゆえ、"符号を維持" という名称になります。
例えば、9 >> 2 の結果は 2 になります。
9 (10 進数): 00000000000000000000000000001001 (2 進数)
--------------------------------
9 >> 2 (10 進数): 00000000000000000000000000000010 (2 進数) = 2 (10 進数)
同様に、-9 >> 2 の結果は符号が維持されるため -3 になります:
-9 (10 進数): 11111111111111111111111111110111 (2 進数)
--------------------------------
-9 >> 2 (10 進数): 11111111111111111111111111111101 (2 進数) = -3 (10 進数)
>>> (0 埋め右シフト)
この演算子は、第 1 オペランドを指定したビット数分だけ右にシフトします。右に溢れたビットは破棄されます。0 のビットを左から詰めます。符号ビットが 0 になりますので、結果は常に正数になります。
非負数では、0 埋め右シフトと符号を維持した右シフトは同じ結果になります。例えば、9 >>> 2 の結果は 2 になり、9 >> 2 と同じです:
9 (10 進数): 00000000000000000000000000001001 (2 進数)
--------------------------------
9 >>> 2 (10 進数): 00000000000000000000000000000010 (2 進数) = 2 (10 進数)
しかし、これは負数に当てはまりません。例えば、-9 >>> 2 の結果は 1073741821 であり、-9 >> 2 の結果 (-3 になります) と異なります:
-9 (10 進数): 11111111111111111111111111110111 (2 進数)
--------------------------------
-9 >>> 2 (10 進数): 00111111111111111111111111111101 (2 進数) = 1073741821 (10 進数)
例
例: フラグとビットマスク
ビット論理演算子は、一連のフラグを作成、操作、あるいは読み取りによく用いられ、それらフラグはバイナリ変数のようなものです。変数は一連のフラグの代わりに用いられますが、バイナリフラグがもつメモリはとても少なく (32倍) なります。
4 つのフラグがあると仮定します:
- フラグ A: 我々は蟻の問題を抱えています
- フラグ B: 我々はコウモリを飼っています
- フラグ C: 我々は猫を飼っています
- フラグ D: 我々はアヒルを飼っています
これらのフラグは一連のビットで表されます: DCBA. フラグがセットされると、それは値 1 になります。フラグがクリアされると、それは値 0 になります。変数 flags が 2 進数値 0101 であるとします:
var flags = 0x5; // 2 進数値 0101
この値は以下を示します:
- flag A は true (蟻の問題を抱えています);
- flag B は false (コウモリを飼っていません);
- flag C は true (猫を飼っています);
- flag D は false (アヒルを飼っていません);
ビット演算は 32 ビットで行われることから、0101 は実際 00000000000000000000000000000101 ですが、先行する 0 は意味のある情報ではないため無視できます。
ビットマスク はフラグの操作や読み取りを可能にするビット列です。一般的に、各フラグ向けの "基本" ビットマスクが定義されます:
var FLAG_A = 0x1; // 0001 var FLAG_B = 0x2; // 0010 var FLAG_C = 0x4; // 0100 var FLAG_D = 0x8; // 1000
新たなビットマスクを、これら基本ビットマスクのビット論鈴演算を用いて作成できます。例えば、ビットマスク 1011 は FLAG_A、FLAG_B、FLAG_D の OR 演算により作成できます:
var mask = FLAG_A | FLAG_B | FLAG_D; // 0001 | 0010 | 1000 => 1011
個々のフラグの値はビットマスクとの AND 演算によって抽出され、そのとき値 1 をもつ各ビットが対応するフラグを "抽出" します。ビットマスクは 0 との AND 演算により、関係のないフラグをマスクします (それゆえ、"ビットマスク" という用語になります)。例えば、ビットマスク 0100 はフラグ C がセットされているかの確認に用いることができます:
// 猫を飼っている場合
if (flags & FLAG_C) { // 0101 & 0100 => 0100 => true
// さまざまな処理
}
複数のフラグをセットしたビットマスクは、"いずれか" を表します。例えば、以下 2 つの例は同等です:
// コウモリか猫を飼っている場合
if ((flags & FLAG_B) || (flags & FLAG_C)) { // (0101 & 0010) || (0101 & 0100) => 0000 || 0100 => true
// さまざまな処理
}
// コウモリか猫を飼っている場合
var mask = FLAG_B | FLAG_C; // 0010 | 0100 => 0110
if (flags & mask) { // 0101 & 0110 => 0100 => true
// さまざまな処理
}
フラグはビットマスクとの OR 演算によって設定でき、値が 1 である各々のビットが対応するフラグを、まだセットされていない場合にセットします。例えば、ビットマスク 1100 はフラグ C と D のセットに用いることができます:
// はい、猫とアヒルを飼っています var mask = FLAG_C | FLAG_D; // 0100 | 1000 => 1100 flags |= mask; // 0101 | 1100 => 1101
フラグはビットマスクとの AND 演算によってクリアでき、値が 0 である各ビットが対応するフラグを、まだクリアされていない場合にクリアします。このビットマスクは、基本ビットマスクの NOT 演算で作成されます。例えば、ビットマスク 1100 はフラグ A と C のクリアに用いることができます:
// いいえ、蟻の問題はありませんし猫を飼ってもいません var mask = ~(FLAG_A | FLAG_C); // ~0101 => 1010 flags &= mask; // 1101 & 1010 => 1000
マスクは ~FLAG_A & ~FLAG_C (ド・モルガンの法則) で作成することもできます:
// いいえ、蟻の問題はありませんし猫を飼ってもいません var mask = ~FLAG_A & ~FLAG_C; flags &= mask; // 1101 & 1010 => 1000
フラグはビットマスクとの XOR 演算によって切り替えることができ、値が 1 である各々のビットが対応するフラグを切り替えます。例えば、ビットマスク 0110 はフラグ B と C の切り替えに用いることができます:
// 以前はコウモリを飼っていませんでしたが今は飼っている、および // 以前飼っていたコウモリとお別れした、また猫にも同じことが起きた場合 var mask = FLAG_B | FLAG_C; flags = flags ^ mask; // 1100 ^ 0110 => 1010
最後に、NOT 演算でフラグを反転することができます:
// パラレルワールドに入りました…… flags = ~flags; // ~1010 => 0101
変換用コード部品
var sBinString = "1011"; var nMyNumber = parseInt(sBinString, 2); alert(nMyNumber); // 11 と表示、すなわち 1011
var nMyNumber = 11; var sBinString = nMyNumber.toString(2); alert(sBinString); // 1011 と表示、すなわち 11
マスク作成の自動化
いくつかの真偽値から多くのマスクを作成しなければならない場合に、この作業を自動化できます:
function createMask () {
var nMask = 0, nFlag = 0, nLen = arguments.length > 32 ? 32 : arguments.length;
for (nFlag; nFlag < nLen; nMask |= arguments[nFlag] << nFlag++);
return nMask;
}
var mask1 = createMask(true, true, false, true); // 11、すなわち1011
var mask2 = createMask(false, false, true); // 4、すなわち 0100
var mask3 = createMask(true); // 1、すなわち 0001
// etc.
alert(mask1); // 11 と表示、すなわち 1011
逆操作アルゴリズム: マスクから真偽値の配列を作成
マスクから真偽値の配列を作成したい場合、以下のコードを用いることができます:
function arrayFromMask (nMask) {
// nMask は -2147483648 から 2147483647 の間でなければならない
if (nMask > 0x7fffffff || nMask < -0x80000000) { throw new TypeError("arrayFromMask - out of range"); }
for (var nShifted = nMask, aFromMask = []; nShifted; aFromMask.push(Boolean(nShifted & 1)), nShifted >>>= 1);
return aFromMask;
}
var array1 = arrayFromMask(11);
var array2 = arrayFromMask(4);
var array3 = arrayFromMask(1);
alert("[" + array1.join(", ") + "]");
// "[true, true, false, true]" と表示、すなわち 11 および 1011
両方のアルゴリズムを同時に検証できます。
var nTest = 19; // マスク var nResult = createMask.apply(this, arrayFromMask(nTest)); alert(nResult); // 19
(Number.toString(2) メソッドがありますので)教育的な目的のみになりますが、真偽値の配列ではなく数値の 2 進表記を含む 文字列を作成するために arrayFromMask アルゴリズムがどのように操作しているかを示します:
function createBinaryString (nMask) {
// nMask は -2147483648 から 2147483647 の間でなければならない
for (var nFlag = 0, nShifted = nMask, sMask = ""; nFlag < 32; nFlag++, sMask += String(nShifted >>> 31), nShifted <<= 1);
return sMask;
}
var string1 = createBinaryString(11);
var string2 = createBinaryString(4);
var string3 = createBinaryString(1);
alert(string1);
// prints 00000000000000000000000000001011, i.e. 11