ビット演算子
ビット演算子は、オペランドを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ビット整数
全てのビット演算子のオペランドは、2進数の補数表現の符号付き32ビット整数に変換されます。 2進数の補数表現では、数値の負の値(例えば、5に対する-5)が、 全てのビットを反転して1を足したものであることを意味します。 (ビットNOTは、1の補数としても知られています) 例えば、下記は10進数の314に相当する2進数ですが、
00000000000000000000000100111010
下記は~314、各桁のビットを反転しています。
11111111111111111111111011000101
最終的に上記に1を足して-314になり、314の2進数の補数となります。
11111111111111111111111011000110
2進数の補数表現は、正の数の場合は左端のビットが0であり、 負の数の場合は左端のビットが1であることを保証してくれます。 そのため、この左端のビットは符号ビットと呼ばれます。
整数0は、全てのビットが0になることで構成されます。
0 (10進数) = 00000000000000000000000000000000 (2進数)
整数-1は、全てのビットが1になることで構成されます。
-1 (10進数) = 11111111111111111111111111111111 (2進数)
数値-2147483648(16進数で表すと-0x80000000)は、左端のビットの1を除き、 全てのビットが0になることで構成されます。
-2147483648 (10進数) = 10000000000000000000000000000000 (2進数)
数値2147483647(16進数で表すと0x7fffffff)は、左端のビットの0を除き、 全てのビットが1になることで構成されます。
2147483647 (10進数) = 01111111111111111111111111111111 (2進数)
-2147483648と2147483647の数値は、符号付き32ビットで表すことが出来る最小と最大の整数になります。
ビット論理演算子
概念上、ビット論理演算子は下記のように動作します。
- オペランドは32ビット整数に変換され、連続したビット(0と1)によって表現されます。
- 1つ目のオペランドの各ビットは、2つ目のオペランドの各ビットと、 1つ目のビットは1つ目のビットへ2つ目のビットは2つ目のビットへ、といったようにペアにされます。
- 演算子は各ビットのペアに対して適用され、その結果はビット毎に構成されます。
& (ビットAND)
ビットの各ペアにおいて、AND演算を実行します。
aとbが両方とも1であれば、a AND b
は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またはbのどちらかが1であれば、a OR b
は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とbが異なる値であれば、a XOR 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を反転します。
NOT演算の真偽テーブルは下記の通りです。
a | NOT a |
---|---|
0 | 1 |
1 | 0 |
9 (10進数) = 00000000000000000000000000001001 (2進数)
--------------------------------
~9 (10進数) = 11111111111111111111111111110110 (2進数) = -10 (10進数)
数値x
をビットNOTすると、-(x + 1)
になります。
例えば、~5
は-6
になります。
下記はindexOfを使用した例になります。
var str = 'rawr';
var searchFor = 'a';
// (-1*str.indexOf('a') <= -1)の代わりの方法
if (~str.indexOf(searchFor)) {
// searchForは文字列内に有り
// (ifの結果は、0以外の値)
} else {
// searchForは文字列内に無し
// (ifの結果は、0)
// (indexOfで見つからない場合の結果は-1であり、それを反転して0)
}
// (~str.indexOf(searchFor))によって返される値
// r == -1
// a == -2
// w == -3
// z == 0 //文字列に含まれない値
ビットシフト演算子
ビットのシフト演算子は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
は9 >> 2
と同じ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進数)
例
例: フラグとビットマスク
ビット理論演算はしばしば、2進数変数のような、一連なりのフラグの作成・操作・読み込みに使用されます。 変数はこれら一連なりのフラグの代わりに使用することが可能であり、 バイナリフラグであれば少ないメモリ量(32倍の違い)で済みます。
下記の4つのフラグがあるとして、考えてみましょう。
- フラグA: 駐車場有り
- フラグB: バス・トイレ別
- フラグC: 南向き
- フラグD: エアコン有り
これらのフラグは、連続したビットDCBA
で表すことが出来ます。
フラグがセットされた場合は1の値を、フラグがクリアされた場合は0の値を持つことにします。
2進数としての値が0101
の場合を考えてみましょう。
var flags = 5; // 2進数は、0101
//
// ||||
//
// DBCA
この値は次のことを示します。
- フラグAは、true (駐車場有り)
- フラグBは、false (バス・トイレが別では無い)
- フラグCは、true (南向き)
- フラグDは、false (エアコン無し)
ビット演算子は32ビットで扱われるため0101
は実際には、
00000000000000000000000000000101
ですが、
先行する0は意味のある情報を含まないことから、無視することが出来ます。
ビットマスクは、フラグを操作または(/且つ)読み込む事が出来る一連なりのビットです。 一般的に、各フラグ用に"プリミティブ"なビットマスクが定義されます。
var FLAG_A = 1; // 0001
var FLAG_B = 2; // 0010
var FLAG_C = 4; // 0100
var FLAG_D = 8; // 1000
新しいビットマスクは、これらプリミティブなビットマスクを元に、
ビット論理演算子を使用して作成することが出来ます。
例えば、ビットマスク1011
は、
FLAG_A
、FLAG_B
、FLAG_D
をOR演算することで作成することが出来ます。
var mask = FLAG_A | FLAG_B | FLAG_D; // 0001 | 0010 | 1000 => 1011
特定のフラグ値は、対応するフラグを"抜き取る"各ビットが1であるビットマスクと、
AND演算することで取り出すことが可能です。
そのビットマスクは、関係の無いフラグを0とAND演算することでマスクアウトします。(それ故、ビットマスクと呼ばれます)
例えば、ビットマスク0100
は、フラグCが設定されているかを確認するために使用することが出来ます。
// もし、南向きの場合は、
if (flags & FLAG_C) { // 0101 & 0100 => 0100 => true
// 処理
}
複数のフラグが設定されたビットマスクは、"either/or"(どちらか一方/または)のように動作します。 例えば、下記の2つの例は同じように処理されます。
// バス・トレイが別、または南向きの場合、
// (0101 & 0010) || (0101 & 0100) => 0000 || 0100 => true
if ((flags & FLAG_B) || (flags & FLAG_C)) {
// 処理
}
// バス・トレイが別、または南向き
var mask = FLAG_B | FLAG_C; // 0010 | 0100 => 0110
if (flags & mask) { // 0101 & 0110 => 0100 => true
// 処理
}
まだ設定がされていないフラグを、設定する各対応フラグが1であるビットマスクをOR演算することで、設定することが可能です。
例えば、ビットマスク1100
はフラグCとDの設定に使用することが出来ます。
// 南向きで、エアコン有り
var mask = FLAG_C | FLAG_D; // 0100 | 1000 => 1100
flags |= mask; // 0101 | 1100 => 1101
まだクリアされていないフラグを、クリアする各対応フラグが0であるビットマスクをAND演算することで、クリアすることが可能です。
このビットマスクは、プリミティブのビットマスクをNOT演算することで作成することが出来ます。
例えば、ビットマスク1010
はフラグ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
切り替えるフラグに対応する各ビットが1であるビットマスクを使用してXOR演算することで、
フラグを入れ替えることが可能です。
例えば、ビットマスク0110
は、フラグBとCを切り替えるのに使用することが出来ます。
// バス・トイレが別で無いなら、別に、
// バス・トイレが別なら、別では無いに。
// 南向きに対しても同様。
var mask = FLAG_B | FLAG_C;
flags = flags ^ mask; // 1100 ^ 0110 => 1010
最後にNOT演算子ですが、これを使用すると各フラグが反転します。
//真逆の条件
flags = ~flags; // ~1010 => 0101
変換用のスニペット
2進数の文字列(String)を、 10進数の数値(Number)に変換します。
var sBinString = "1011";
var nMyNumber = parseInt(sBinString, 2);
alert(nMyNumber); // 11(10進数)を出力, すなわち、1011(2進数)
10進数の数値(Number)を、 2進数の文字列(String)に変換します。
var nMyNumber = 11;
var sBinString = nMyNumber.toString(2);
alert(sBinString); // 1011(2進数)を出力、すなわち、11(10進数)
マスクの作成を自動化
幾つかの真偽値から多くのマスクを作成しないといけない場合に、 このプロセスを自動化することが可能です。
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
// 他
alert(mask1); // 11を出力、すなわち1011
逆方向アルゴリズム: マスクから真偽値の配列を作成
もし、マスクから真偽値の配列(Array)を作成したい場合は、 下記のコードを使用することが出来ます。
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(10進数)、1011(2進数)
同時に両方のアルゴリズムを検証することも可能です。
var nTest = 19; // our custom mask
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);
// 00000000000000000000000000001011を出力。すなわち11
仕様
ブラウザ互換性
機能 | Chrome | Firefox (Gecko) |
IE | Opera | Safari |
---|---|---|---|---|---|
基本 | ◯ | ◯ | ◯ | ◯ | ◯ |
機能 | Android | Chrome for Android |
Firefox Mobile |
IE Mobile |
Opera Mobile |
Safari Mobile |
---|---|---|---|---|---|---|
基本 | ◯ | ◯ | ◯ | ◯ | ◯ | ◯ |
関連項目
- Logical operators
© 2017 Mozilla Contributors
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
このページは、ページトップのURL先のMozilla Developer Network(以下、MDN)のコンテンツを翻訳した内容を基に構成されています。 構成について異なる点も含まれますので、下記の項目を確認し、必要に応じて元のコンテンツをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。
- 特定のブラウザに特化しすぎている情報やあまりにも古い情報、 または試験的に導入されているようなAPIや機能については、省略していることがあります。
- 例やデモについて、実際にページ内で動作させる関係で一部ソースコードを変更している場合や、 その例で使用しているコンテンツの単語や文章などを日本人向けに変更しいてる場合があります。
- MDNの更新頻度が高いため、元のコンテンツと比べ情報が古くなっている可能性があります。
- "訳注:"などの断わりを入れた上で、日本人向けの情報の追記を行っている事があります。