二項ビット演算子

概要

2 進ビット演算子は、2 進ビットを直接計算するために使用され、合計 7 つあります。

  • バイナリ OR 演算子 (or): 記号は | で、両方の 2 進ビットが 0 であれば結果は 0、それ以外の場合は 1 であることを意味します。
  • 二項 AND 演算子 (and): 記号は「&」です。これは、両方の 2 進ビットが 1 の場合、結果は 1 になり、それ以外の場合は 0 になることを意味します。
  • Binary No Operator (not): 記号は ~ で、2 進ビットの否定を意味します。
  • 排他的 OR 演算子 (xor): 記号は「^」です。これは、2 つのバイナリ ビットが同じでない場合、結果は 1 になり、そうでない場合は 0 になることを意味します。
  • 左シフト演算子 (左シフト): 記号は << です。詳細については、以下の説明を参照してください。
  • 右シフト演算子 (右シフト): 記号は >> です。詳細については、以下の説明を参照してください。
  • 先頭ゼロ埋め右シフト演算子 (ゼロ埋め右シフト): 記号は >>>> です。詳細については、以下の説明を参照してください。

これらのビット単位の演算子は各ビットを直接処理するため、非常に低レベルの演算であるため、非常に高速であるという利点がありますが、欠点は、多くの状況で使用できないことです。そうしないとコードが作成できなくなります。理解とトラブルシューティングが難しい。

特に注意が必要なことは、ビット単位の演算子は整数に対してのみ機能するため、演算子が整数でない場合は、実行前に自動的に整数に変換されます。また、JavaScript 内では値は 64 ビット浮動小数点数の形式で格納されますが、ビット演算を実行する場合は 32 ビットの符号付き整数に対して演算が実行され、戻り値も 32 ビットの符号付き整数になります。 。

i = i | 0;

上記のコード行の意味は、「i」(整数か小数かを問わず)を 32 ビット整数に変換することです。

この機能を使用すると、任意の値を 32 ビット整数に変換する関数を作成できます。

関数 toInt32(x) {
  x 0 を返します。
}

上記の関数は、任意の値と「0」の間で OR 演算を実行し、値を 32 ビット整数に自動的に変換します。この機能の使い方は以下の通りです。

toInt32(1.001) // 1
toInt32(1.999) // 1
toInt32(1) // 1
toInt32(-1) // -1
toInt32(Math.pow(2, 32) + 1) // 1
toInt32(Math.pow(2, 32) - 1) // -1

上記のコードでは、toInt32 で小数を整数に変換できます。一般の整数の場合、戻り値に変更はありません。 2の32乗以上の整数の場合、32桁を超える桁は四捨五入されます。

二項 OR 演算子

バイナリ OR 演算子 (|) は 2 つの演算子をビットごとに比較し、2 つのバイナリ ビットの 1 つが 1 である限り 1 を返し、それ以外の場合は 0 を返します。

0 | 3 // 3

上記のコードでは、「0」と「3」のバイナリ形式はそれぞれ「00」と「11」であるため、バイナリ OR 演算を実行すると、結果は「11」(つまり「3」)になります。

ビット演算は整数に対してのみ有効です。小数が見つかった場合、小数部分は破棄され、整数部分のみが保持されます。したがって、小数と「0」のバイナリ OR 演算は、数値の小数部分を削除する、つまり整数を取得することと同じです。

2.9 | 0 // 2
-2.9 | 0 // -2

この丸め方法は、最大 32 ビット整数値「2147483647」を超える数値には適用されないことに注意してください。

2147483649.4;
// -2147483647

二項 AND 演算子

バイナリ AND 演算子 (&) のルールは、2 つの演算子をビットごとに比較することです。2 つのバイナリ ビットのうちの 1 つが 0 である限り、0 を返し、それ以外の場合は 1 を返します。

0 & 3 // 0

上記のコードでは、0 (バイナリ 00) と 3 (バイナリ 11) のバイナリ AND 演算の結果は 00 (つまり 0) になります。

二項演算子なし

2 項否定演算子 (~) は、各 2 進ビットを反対の値に変換します (「0」は「1」になり、「1」は「0」になります)。返される結果は、コンピューターの内部数値表現メカニズムに関係しているため、理解するのが難しい場合があります。

~ 3 // -4

上の式は、「3」に対して二項否定演算を実行し、「-4」を取得します。この結果が生じる理由は、ビット演算中に、演算を実行する前に JavaScript が内部ですべての演算子を 32 ビット 2 進整数に変換するためです。

'3' の 32 ビット整数形式は、'0000000000000000000000000000011' になります。2 項否定演算の後、'11111111111111111111111111111100' が得られます。最初のビット (符号ビット) が 1 であるため、この数値は負の数になります。 JavaScript は内部で 2 の補数形式を使用して負の数値を表します。つまり、この数値から 1 を減算し、再度否定し、負の符号を追加して、この負の数値に対応する 10 進数値を取得する必要があります。この数値から 1 を引くと、「111111111111111111111011」となります。これを再度反転すると、負の符号を追加すると、「0000000000000000000000100」となります。このような処理は面倒ですが、数値とその否定を足すと -1 になると覚えておけば問題ありません。

~ -3 // 2

上の式は次のように計算できます。「-3」の逆数値は「-1」から「-3」を引いた値に等しく、結果は「2」になります。

整数に対して 2 つの連続した 2 項否定演算を実行して、整数自体を取得します。

~~3 // 3

すべてのビット単位の演算は整数に対してのみ有効です。 2 項否定演算で小数が発生すると、小数部分も破棄され、整数部分のみが残ります。したがって、小数に対して 2 つの連続した 2 進否定演算を実行すると、丸め効果を得ることができます。

~~2.9 // 2
~~47.11 // 47
~~1.9999 // 1
~~3 // 3

二項否定演算子を使用した丸めは、すべての丸め方法の中で最も高速です。

文字列に対して 2 項否定演算を実行するには、JavaScript エンジンはまず Number 関数を呼び出して文字列を数値に変換します。

// ~Number('011') と同等
~'011' // -12

// ~Number('42 猫') と同等
~'42 匹の猫' // -1

// ~Number('0xcafebabe') と同等
~'0xcafebabe' // 889275713

// ~Number('deadbeef') と同等
~'デッドビーフ' // -1

Number 関数を使用して文字列を数値に変換する規則については、「データ型変換」の章を参照してください。

他のタイプの値の場合も、二項否定演算は最初に「Number」を使用して数値に変換されてから処理されます。

// ~Number([]) と同等
~[] // -1

// ~Number(NaN) と同等
~NaN // -1

// ~Number(null) と同等
~null // -1

XOR 演算子

排他的 OR 演算 (^) は、2 つのバイナリ ビットが異なる場合は 1 を返し、同じ場合は 0 を返します。

0 ^ 3 // 3

上の式では、「0」(バイナリ「00」)と「3」(バイナリ「11」)のXOR演算が行われ、それぞれのバイナリビットが異なるため、「11」(つまり「3」)が得られます。

「XOR 演算」には、2 つの数値「a」と「b」に対して 3 回の連続した XOR 演算、「a^=b; a^=b;」を実行する特別なアプリケーションがあります。それらの値を交換します。これは、「XOR 演算」を使用すると、一時変数を導入せずに 2 つの変数の値を交換できることを意味します。

変数 a = 10;
var b = 99;

a ^ = b、b ^ = a、a ^ = b;

// 99
b // 10

これは、2 つの変数の値を交換する最も速い方法です。

XOR 演算は丸めにも使用できます。

12.9 ^ 0 // 12

左シフト演算子

左シフト演算子 (<<) は、数値のバイナリ値を指定された桁数だけ左に移動し、末尾に 0 を埋め込みます。つまり、指定された 2 の累乗を乗算します。 。左に移動すると、最上位の符号ビットも一緒に移動します。

// 4 の 2 進形式は 100 です。
// 1000 まで 1 桁左にシフトします (つまり、10 進数で 8)
// 2 の 1 乗に相当します。
4 << 1
// 8

-4 << 1
// -8

上記のコードでは、「-4」を 1 桁左にシフトして「-8」を取得します。これは、「-4」のバイナリ形式が「111111111111111111111111111100」であるためです。左に 1 桁シフトすると、「1111111111111111111111111111000」が得られます。数値を10進数に変換すると(1をマイナスしてマイナス符号を付けて減算)、「-8」となります。

0 ビットを左にシフトすると、値を 32 ビット整数に変換することと同じになり、これは丸めに相当し、正の数値と負の数値の両方に有効です。

13.5 << 0
// 13

-13.5 << 0
// -13

左シフト演算子はバイナリ値の場合に非常に便利です。

var color = {r: 186g: 218b: 85};

// RGB から 16 進数へ
// (1 << 24) は結果が 6 桁になるようにするために使用されます
var rgb2hex = function(r, g, b) {
  return '#' + ((1 << 24) + (r << 16) + (g << 8) + b)
    .toString(16) // まず 16 進数に変換してから文字列に戻ります
    .substr(1); //文字列の最上位ビットを削除し、次の 6 つの文字列を返します。
}

rgb2hex(color.r, color.g, color.b)
// "#bada55"

上記のコードは、左シフト演算子を使用して色の RGB 値を 16 進値に変換します。

右シフト演算子

右シフト演算子 (>>) は、数値の 2 進値を指定された桁数だけ右に移動することを意味します。正の数の場合、ヘッダーは「0」で埋められ、負の数の場合、ヘッダーは「1」で埋められます。右シフト演算子は基本的に、指定された「2」のべき乗で除算することと同等です (最上位ビットである符号ビットがシフトに関与します)。

4>>1
// 2
/*
// 4 のバイナリ形式は 00000000000000000000000000000100 なので、
// 右に 1 ビットシフトして、00000000000000000000000000000010 を取得します。
// 10進数の2です
*/

-4>>1
// -2
/*
// -4 のバイナリ形式は 11111111111111111111111111111100 であるため、
// 右に 1 桁移動し、先頭に 1 を加えて 11111111111111111111111111111110 を取得します。
// 10 進数で -2 です
*/

右シフト演算は、2 の整数除算演算をシミュレートできます。

5>>1
// 2
// 5 / 2 = 2 に相当

21>>2
// 5
// 21 / 4 = 5 に相当

21>>3
// 2
// 21 / 8 = 2 に相当

21>>4
// 1
// 21 / 16 = 1 に相当

先頭ゼロパディング右シフト演算子

右シフト演算子 (>>>>) と先頭にゼロを埋め込む右シフト演算子 (>>) の違いは 1 つだけです。つまり、数値の 2 進形式が右に移動する場合です。 、先頭には常にゼロが埋め込まれます。符号ビットを考慮してください。したがって、この演算は常に正の値を返します。正の数の場合、この演算の結果は右シフト演算子 (>>) とまったく同じになりますが、違いは主に負の数にあります。

4 >>> 1
// 2

-4 >>> 1
// 2147483646
/*
// -4 のバイナリ形式は 1111111111111111111111111111100 であるため、
// 符号ビットを 1 ビット右にシフトして、011111111111111111111111111111110 を取得します。
// これは 10 進数で 2147483646 です。
*/

この操作は実際に値を 32 ビット符号なし整数に変換します。

負の整数がコンピューターの内部でどのように保存されているかを確認する最も速い方法は、この演算子を使用することです。

-1 >>> 0 // 4294967295

上記のコードは、-1 が 32 ビット整数として使用される場合、内部記憶形式は符号なし整数形式を使用して解釈され、値は 4294967295 (つまり、(2^32)-1、これは、11111111111111111111111111111111 に等しい)。

スイッチ機能

ビット演算子は、オブジェクトのプロパティを設定するためのスイッチとして使用できます。

オブジェクトに 4 つのスイッチがあり、それぞれが変数であると仮定します。次に、各ビットがスイッチに対応する 4 桁の 2 進数を設定できます。

var FLAG_A = 1;
var FLAG_B = 2;
var FLAG_C = 4;
var FLAG_D = 8;

上記のコードは、4 つのスイッチ A、B、C、D を設定します。各スイッチは 2 進ビットを占有します。

次に、バイナリ AND 演算を使用して、現在の設定が指定したスイッチをオンにするかどうかを確認できます。

var flags = 5; バイナリ 0101;

if (フラグ & FLAG_C) {
  // ...
}
// 0101 & 0100 => 0100 => true

上記のコードは、スイッチ「C」がオンになっているかどうかをチェックします。オンにすると「true」を返し、それ以外の場合は「false」を返します。

ここで、3 つのスイッチ「A」、「B」、「D」をオンにする必要があると仮定して、マスク変数を構築できます。

var マスク = FLAG_B |
// 0001 | 1000 => 1011

上記のコードは、3 つの変数「A」、「B」、「D」に対してバイナリ OR 演算を実行し、バイナリのマスク値「1011」を取得します。

マスクを使用すると、バイナリ OR 演算により、指定されたスイッチが確実にオンになります。

フラグ = フラグマスク |

上記のコードでは、計算後に得られた変数「flags」は、3 つのスイッチのバイナリ ビットがすべてオンであることを表します。

バイナリ AND 演算により、スイッチ設定と異なる現在の設定のすべての項目をオフにすることができます。

flags = フラグとマスク;

XOR 演算は現在の設定を切り替えることができます。つまり、最初の実行では現在の設定の反対の値を取得し、2 回目の実行では元の値を取得できます。

flags = フラグ ^ マスク;

2 項否定演算では、現在の設定を反転できます。つまり、元の設定は「0」ですが、演算後は「1」になり、演算後は「0」になります。

フラグ = ~フラグ;

参考リンク


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0