JavaScript/ビット演算

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動
演算子 JavaScript
ビット演算
文字列
JavaScript > ビット演算

ビット演算[編集]

注:この項目は主にJavaScriptにおける2進法の計算方法を解説するが、ページとしての独立性が高く、ほかの項目との関連が薄い。 そのため、必ずしもこの項を読む必要は無く、この場では読み飛ばして学習の程度にあわせて後々に読んでも構わない。

ビット演算[編集]

JavaScriptはビット(二値)演算をサポートしている。

  • & ビットごとのAND(論理積)
  • | ビットごとのOR(論理和)
  • ^ ビットごとの排他的 OR(排他的論理和)
  • << 左シフト
  • >> 右シフト
  • >>> 符号なし右シフト演算子
  • ~ 1の補数を得る

また、次の様な複合代入演算子も用意されている

  • &= ビットごとのAND演算をして代入
  • |= ビットごとのOR演算をして代入
  • ^= ビットごとの排他的ORをして代入
  • >>= 右シフトして代入
  • <<= 左シフトして代入
  • >>>= 符号なし右シフト演算子

JavaScriptでビット演算を行ううえで注意すべきことは、ビット演算を行う前にNumber型の値を一度32ビットの整数に変換をする事である。 これは、現在のプロセッサが32ビットの整数型を扱いやすい事を考えられて作られていると思われる。

ビット演算を既に理解している場合はこれ以降の解説は必要ないと思われるが、プログラミング初学者のために以降はビット演算の基本的な解説を行う。

2進法の表記法[編集]

コンピューターは、その回路構成を単純にするため通常内部的に2進法を用いているため、2進数で計算できるものはこちらで計算した方が高速に動作するため 、通常こちらを利用する。 二進法とは、10進法とは違い2の倍数で桁上げを行う数の表記法である。理解のために、10進法と、2進、3進、4進法での数の表記の対応表、さらに8進法」、16進法の表記を載せる。

10進法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
2進法 1 10 11 100 101 110 111 1000 1001 1010 1011 1100 1101 1110 1111 10000 10001 10010 10011 10100
3進法 1 2 10 11 12 20 21 22 100 101 102 110 111 112 120 121 122 200 201 202
4進法 1 2 3 10 11 12 13 20 21 22 23 30 31 32 33 100 101 102 103 110
8進法 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17 20 21 22 23 24
16進法 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14

2進法は、大きな数になるほど桁数が増えるため、記述を短く簡単に抑えるため通常は16進法を使って記述を行う。 16進法では、9を超える数には順にa,b,c,d,e,fと記号を振ってあり1~15までを一桁で表せる。 16進法を使用する場合には、数字の前に0xを付けて表記する。

過去のJavaScriptの標準仕様で、C言語と同じ 0 を前置する8進法表記が可能であるがECMA-262第3版で削除された。 その後、ECMA-262第6版で 0b (または 0B)を前置する2進法表記とともに 0o (または 0O)を前置する8進法表記で復活した。

補数[編集]

補数とは2進法で、負の値を表現するための非常に巧みな方法であるが、アセンブラでのプログラム等非常に低レベルでのプログラミング以外で実際に使用される事は稀である。 簡単のため、この項の解説はあえて省略をする。

ビットシフト[編集]

ビットシフトとは、2進数のビットをそのまま、右側、または左側にずらず事を言う。空いたビットには0が埋められる。 これは実質、2の倍数の乗算、余算に等しい。ただし、こちらの方が高速に動作する。

1 << 2;  // 1が2ビット左シフトするため、2進数で「100」つまり4が返る
0xd >> 2;  // 10進法では13。2進法で「1101」これを右シフトして2進数で「11」、つまり10進法の3が返る 

JavaScriptではビットシフトを行っても正負の記号は保存される。 しかし、符号なし右シフト演算子(>>>)を使った場合にはこの結果は異なる。

-10 >> 1;   // -5が返る
-10 >>> 1;  // 2147483643が返る 

これは、先頭にある正負の記号を表すビットもシフトさせるためである。

論理演算[編集]

実際には、ビット積とビット和を使用する事が多いのでこちらに解説の重点を置く。

論理積を例に挙げる。 論理積の結果を表に表すと次の様になっている。

命題P 命題Q P & Q
1 1 1
1 0 0
0 1 0
0 0 0

論理積の演算子は"&"であるから、実際の演算を行うと次の様になる。

1 & 1;  // 1が返る
1 & 0;  // 0が返る
0 & 1;  // 0が返る
0 & 0;  // 0が返る 

同じように、論理和、排他的論理和の計算結果を挙げると次の様になっている。

// 論理和の演算
1 | 1;  // 1が返る
1 | 0;  // 1が返る
0 | 1;  // 1が返る
0 | 0;  // 0が返る

// 排他的論理和の演算
1 ^ 1;  // 0が返る
1 ^ 0;  // 1が返る
0 ^ 1;  // 1が返る
0 ^ 0;  // 0が返る 

これらの演算はビットごとに行われるため、通常の四則演算とは違った考え方で結果を捉える必要がある。 例として10進数で7を挙げる、これを2進数に直すと、「111」となる。 同じように2も2進数では「10」と表記されるため、このビット積「7&2」は、ビットごとの計算が行われるため次の様になる。

数字 3ビット目 2ビット目 1ビット目
7 1 1 1
2 0 1 0
5 0 1 0
7 & 2; // 2進数では111 & 010 、この結果は2進数で「010」なので「2」である 

この性質を利用すればビット積で末尾1ビットを残すことで、奇数、偶数の判別が可能である。 この様にして必要なビットのみを取り出す操作をビットマスクと呼ぶ。

num & 1; // 1なら奇数、0なら偶数 

0となった値を1にするにはビット和を利用する。

10 | 13; // 2進数では、1010 | 1101であるから、結果は「1111」、つまり15が返る

2進法よりも簡潔な記述を行うため、実際のコードでは次の様に16進法で記述を行う。

0xa | 0xd; // 0xfが返る

この仕掛けを利用すれば、一つの数字データに複数のデータを格納できる事に気づく。 例えば、コンピュータ内部で色を扱う場合の例で、数値型データの中から、32ビットの数値を8ビットごとに区切ってそれぞれ、赤、緑、青、透明度を格納している。 (これをARGB系と呼んでいる) 取り出す場合には、次の様にコードを記述する。色を一つの数字型にまとめて格納することが出来る。

/**
 * ビットデータを、色を含んだ構造体に変更する
 */
function getColor( color ){
  return { 
    r:(color & 0xff000000) >> 24, // Red
    g:(color & 0xff0000) >> 16,   // Green
    b:(color & 0xff00) >> 8,      // Blue
    a:color & 0xff                // Alpha
  };
}
getColor( color ).r;  // 赤を取り出し

実際にはビット演算には他にも数多くのテクニックが存在するが、今回のその解説は省略した。 JavaScriptでのプログラミングでは、スクリプトの評価の方が処理時間をとっているためC言語等の様に高速化手法としては使われない。 そのため、InternetExplolerでActiveXオブジェクトとのデータのやりとりなど、他の言語環境とのデータのやり取りに使われることもあるが、実際にこれらの演算手法を使う機会は他の言語より少ない。 基本的には、配列などの他の方法を使うよりこちらの方が鮮やかに記述できる場合に利用すべきである。

世界最初のコンピューターは二進法でない!?

世界最初のコンピューターとして現在知られているENIAC(エニアック)は、10進数(3増し符号)での計算方法を行っています。 しかし、現在の計算機では2進数で計算を行っています。 この変化は実際にかなり早い時期におこっており、1946年のENIACの登場の後に、2進数での設計の方が回路構成が単純になるという論文が登場しており、後継の1951年のEDVAC(エドバック)では既に2進数での計算に移っています。