JavaScript/ビット演算

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

ビット演算[編集]

Wikipedia
Wikipedia
ウィキペディアビット演算の記事があります。

JavaScriptには、2進数(ビットパターン)を操作するためのビット演算子が用意されています。これらの演算子は、他の演算子ほど頻繁に使用されるものではないので、必要ない場合はこのセクションを読み飛ばしてもかまわないです。

ビット演算[編集]

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

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

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

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

JavaScriptでビット演算を行ううえで注意すべきことは、ビット演算を行う前にNumber型の値が32ビット整数に強制変換されるということです。 これは、JavaScriptの数値型は倍精度浮動小数点型(64ビットFloat)で、仮数部が53ビットであり、それ以下で最大の2の冪乗が32ビットという理由からです。

2進法の表記法[編集]

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

異なる基数での整数リテラル表記
10進法 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
2進法 0b0 0b1 0b10 0b11 0b100 0b101 0b110 0b111 0b1000 0b1001 0b1010 0b1011 0b1100 0b1101 0b1110 0b1111 0b10000 0b10001 0b10010 0b10011 0b10100
8進法 0o0 0o1 0o2 0o3 0o4 0o5 0o6 0o7 0o10 0o11 0o12 0o13 0o14 0o15 0o16 0o17 0o20 0o21 0o22 0o23 0o24
16進法 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf 0x10 0x11 0x12 0x13 0x14

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進数での計算に移っています。


テンプレート:NavNav