2022/03/13(日) JSprimer(演算子③-ビット演算子-)

前回前々回の記事の続きです。

ビット演算子

ビット演算子では、オペランドである数値を符号付き32ビット整数(0と1からなる32個のビットの集合)として扱います。 符号付き32ビット整数では、先頭の最上位ビット(一番左のビット)は符号を表し、0の場合はの値、1の場合はの値であることを示しています。 符号付き32ビット整数ではの数値は、2の補数形式という形式で表現されます。 2の補数とは、それぞれのビットを反転して1ビットを足した値となります。

  • オペランド32ビット整数に変換され、連続したビット(0と1)によって表現されます。(ビット演算子で計算されるオペランドは、符号付き32ビット整数に変換)
  • Aというオペランドの各ビットは、Bというオペランドの各ビットと対応して計算されます。つまり、Aというオペランドの1つ目のビットはBというオペランドの1つ目のビットといったようにペアごとに計算が行われるということです。
  • 演算結果は、10進数の数値に変換して返します。

符号付き32ビット整数への変換は、以下の2つの例を参考に解説していきます。

例①
10進数での1という数値は、符号付き32ビット整数のビット(32桁の2進数の数値)で表すと以下のようになります。

  • 10進数での1という数値は0000_0000_0000_0000_0000_0000_0000_0001 のような32ビットの集合になります。
  • 先頭の最上位ビット(一番左のビット)は符号を表しているため、正の数を表す0が付いています。

例②
10進数での-1という数値は、符号付き32ビット整数のビット(32桁の2進数の数値)で表すと以下のようになります。
マイナスの数値をビットで表すときは2の補数形式を使います。 1. 表したいマイナスの数値に対応する正の数値の各ビット(0か1の数値)を反転させる 2. 1ビットを足す

  • 10進数の1は、符号付き32ビット整数では0000_0000_0000_0000_0000_0000_0000_0001となる
  • 0000_0000_0000_0000_0000_0000_0000_0001 の各ビットを反転(0を1に、1を0に反転)すると 1111_1111_1111_1111_1111_1111_1111_1110 となる
  • これに1ビットを足すと 1111_1111_1111_1111_1111_1111_1111_1111 となる
  • 最終的に-1符号付き32ビット整数1111_1111_1111_1111_1111_1111_1111_1111となります。

そもそもビット演算がわからなかったので、深掘りしてみました🔍

ビット演算とは?

ビット演算とは、主にコンピュータ上で行われる演算の一つで、対象データをビット列(2進数の0と1の羅列)とみなして、ビットの移動やビット単位の論理演算などを行うもの。
e-Words-ビット演算 【bitwise operation】

  • コンピューターの世界では、0か1の数値を複数組み合わせて、さまざまなデータを扱っています。これは、0か1の数値しかコンピューターが判別でき無いからです。
  • この0か1が入る箱のことをビットと呼びます。例えば4ビットというと、0か1が入る箱が4つあるということです。また、8ビット集まると、その箱の塊のことをバイト(byte)と呼ぶようになります。8ビットは、1バイトです。
  • 格納できる数値が2つしか無いからこそたくさん箱を組み合わせることで複雑なデータを扱うことが出来ます。
  • 0か1の2つの数値を扱うことから2進数を使って表すとも言います。私たち人間が普段扱うことが多いのは、0〜9までの10つの数値を扱う10進数です。
  • 0か1の数値の塊を計算する演算子のことをビット演算子と言います。ビット演算の計算は、0か1の数値しかないので、1であれば0に、0であれば1にスイッチする計算方法になります。電気のONとOFFを切り替えることをイメージすると分かりやすいです。
    「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典-ビット演算

❶ビット論理積(&)

ビット論理積演算子(&)はビットごとのAND演算した結果を返します。 AND演算では、オペランドの各ビットがどちらも1の場合は1となり、それ以外の場合は0となります。

  • AND演算では、aとbが両方とも1の時のみ、1を返します。それ以外は、0を返します。

Image from Gyazo

console.log(15     & 9);      // => 9
// 同じ位の各ビット同士をAND演算する(上位の`0`は省略)
// 1111
// 1001
// ----
// 1001
console.log(0b1111 & 0b1001); // => 0b1001
  • 上記のコードでは、10進数の15と9をAND演算しています。
  • 15は、符号付き32ビット整数では0000_0000_0000_0000_0000_0000_0000_1111となります。
  • 9は、符号付き32ビット整数では0000_0000_0000_0000_0000_0000_0000_1001となります。
  • これらをAND演算した結果は0000_0000_0000_0000_0000_0000_0000_1001となり、10進数の値である9を返します。

❷ビット論理和(|)

ビット論理和演算子(|)はビットごとのOR演算した結果を返します。 OR演算では、オペランドの各ビットがどちらか片方でも1の場合は1となり、両方とも0の場合は0となります。

  • OR演算では、aまたはbのどちらかが1であれば1を返し、aまたはbのどちらも0である場合にのみ0を返します。 Image from Gyazo

    ❸ビット排他的論理和(^)

    ビット排他的論理和演算子(^)はビットごとのXOR演算した結果を返します。 XOR演算では、オペランドのビットが異なるなら1、両方とも同じなら0となります。

  • XOR演算は、aとbの2つのビットを比較して同じ(0同士か1同士)だったら0、違ったら1を返します。 Image from Gyazo

❹ビット否定(~)

各ビットに対して、NOT演算を実行します。 NOT aは、aを反転します。

  • NOT演算は、aという1つのビットを比較してaが0の時に1を、0の時に1のように値を反転させて返します。 Image from Gyazo

ビット否定演算子(~)の性質を利用したindexOfメソッド

文字列(Stringオブジェクト)が持つindexOfメソッドは、マッチする文字列を見つけて、そのインデックス(位置)を返すメソッドです。 このindexOfメソッドは、検索対象が見つからない場合には-1を返します。

const str = "森森本森森";
// 見つかった場合はインデックスを返す
// 文字列str内に本と一致する文字列が見つかったので、その発見位置(インデックス番号)を返す
console.log(str.indexOf("本")); // => 2
// 見つからない場合は-1を返す
console.log(str.indexOf("火")); // => -1

ES2015では、文字列(Stringオブジェクト)にincludesメソッドが実装されました。 includesメソッドは指定した文字列が含まれているかを真偽値で返します。

const str = "森森木森森";
if (str.includes("木")) { // インデックスではなく、真偽値で結果を返す
    console.log("木を見つけました");
}

❺左シフト演算子(<<)

左シフト演算子は、数値であるnumをbitの数だけ左へシフトします。 左にあふれたビットは破棄され、0のビットを右から詰めます。

num << bit;

❻右シフト演算子(>>)

右シフト演算子は、数値であるnumをbitの数だけ右へシフトします。 右にあふれたビットは破棄され、左端のビットのコピーを左から詰めます。 左端のビットのコピーを使うため、常に符号は維持されます。

num >> bit;

❼ゼロ埋め右シフト演算子(>>>)

ゼロ埋め右シフト演算子は、数値であるnumをbitの数だけ右へシフトするのは右シフト演算子(>>)と同じです。 異なる点としては右にあふれたビットは破棄され、0のビットを左から詰めます。 左端のビットは0となるため、常に正の値となります。

num >>> bit;

まとめ

Image from Gyazo

参考

JSPrimer-ビット演算子

朝会ブログ

js STUDIO-ビット演算子

「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典-ビット演算

e-Words-ビット演算 【bitwise operation】