ビット演算


2進数の操作

コンピュータが機械語(論理的な2進数)で動作していることからも
ビットの操作というは、プログラムの中でも重要な項目です

JavaScript は C 言語のように低水準名言語ではないので
そう頻繁にビット演算を行う必要もないのですが、場合によっては非常に有効です

演算子 記号
ビットごとの AND &
左シフト <<
ビットごとの NOT ~
ビットごとの OR |
右シフト >>
ビットごとの XOR ^
右シフト >>>

たとえば、ビットごとの AND は、ビットレベルで論理積を求めます
これはどういうことかというと、数値を2進数として考え
二つのオペランドの各ビットに対し双方が 1 であれば 1、そうでなければ 0 という結果を出します

論理積の演算は、あるビットを削除したりする場合に便利です
alert(0x57 & 0x0F);


これは、二つの16進数の数値 57 と 0F を論理積で演算しています
これらの16進数は、2進数に変換すると 0101 0111 & 0000 1111 という計算になっています
この形で、コンピュータはビット演算を行います

0101 0111
&0000 1111

0000 0111

というように、左辺と右辺のオペランドが演算されています
論理積は双方のビットが 1 でなければ 0 となるので、0x0F と演算させた場合
必ず上位4ビットは 0 となるため、上位4ビットを論理演算で削ることができます

ビットごとの NOT を使えば、全てのビットが反転し
ビットごとの OR を使えば、ビット単位の論理和を求めることができます
ビットごとの XOR は、双方のオペランドの排他的論理和を求めます
var bits = 0x55;
alert(~bits);
alert(bits | 0x0F);
alert(bits ^ 0xF0);


0x55 という16進数は、2進数で 0101 0101 というビット列になります
これを論理否定で反転すると 1010 1010 というビット列になります

OR の場合は、一方のビットが 1 であれば 1 になるため
0101 0101 | 0000 1111 という計算の結果 0101 1111 というビット列になります
論理和は、指定したビットを ON (つまり1)にする時に使用します

最後の排他的論理 (XOR) とは、双方のビットが異なるときに 1というものです
双方が 1 または 0 どうしの場合は 0 になります
すなわち 0101 0101 ^ 1111 0000 の結果は 1010 0101 となります

ところで、最初の alert(^bits) の結果、1010 1010、10進数の 170 を期待します
しかし、どういうわけか -86 という奇妙な数値を表示します
これは、コンピュータが負数を2の補数で表現していることが原因です

詳しくは、コンピュータ化学を学習していただきたいのですが
あるビットの2の補数を求めると、その数の負数を表現できます
2の補数を求めるには、ビットを反転して 1 加算します
1010 1010 の2の補数は 0101 0110 であり、10進数の 86 であることがわかります

コンピュータは、最上位ビットが 1 の時、この数が負数であると判断します
0101 0110 の2の補数は 1010 1010 であり、結果として -86 であることがわかりますね
ただ、JavaScript は数値や変数の管理はすべてスクリプトが管理するため
C 言語のような場合を除いて、このような知識は必要ないでしょう


ビットシフト

さて、次はビットシフトを簡単に説明しましょう
ビットシフトはその名のとおり、ビット列をそのまま左右に移動させる演算です

expression1 >> expression2
expression1 << expression2

>> は expression1 を expression2 だけ右にシフトします
<< は、expression1 を expression2 だけ左にシフトします
expression1 と expression2 には、それぞれ式を指定します

例えば、ビット列 0000 1100 を左に2シフトすると 0011 0000 となります
上位2ビットは破棄され、空いた下位2ビットは 0 で埋められます
alert(0x02 << 2);
alert(0xFF >> 1); 


最初の 0x02 << 2 は、0010 というビットを左に2回シフトします
結果として 1000 となるため、10進数で 8 が表示されます
次の 0xFF >> 1 という計算は、1111 1111 を右に1回シフトし
0111 1111 という結果になっています(当然、溢れた分は破棄されています)

ここで、2進数の特性から左シフトは2乗算、右シフトは2除算することになります
また、数値変数は 32 ビットなのですが、通常の演算はこれを意識する必要はありません
しかし、ビット演算では関与しない上位ビットなどの存在も非常に重要になります

右シフトを行う場合、シフト演算子は右に溢れたビットを破棄しますが
左の空き部分は 0 ではなく、符号と同じビットで埋める のです
こうすることで、負数のシフトも符号を気にすることなく演算できます
このような、符号を保存したシフトを専門的には算術シフトと呼びます

この他に、符号を無視して最上位の空きビットを 0 で埋める右シフトも存在します
この演算には >>> 演算子を使用します

expression1 >>> expression2

この演算子による右シフトは、通常の右シフトと異なり
最上位のビット列には、シフトした分 0 が挿入されます
alert(-16 >> 1);
alert(-16 >>> 1);


最初の -16 >> 1 は、1111 0000 を右にシフトして 1111 1000 になっただけです
上位ビットにできた空きビットには、符号と同じビットが挿入されます

ところが、-16 >>> 1 の場合は厳密に論理シフトされます
-16 の32ビット 1111 1111 1111 1111 1111 1111 1111 0000 を右に1シフトするため
0111 1111 1111 1111 1111 1111 1111 1000 というビットになっています
最上位ビットが 0 なので、これは10進数正数の 2147483640 という数値になります



前のページへ戻る次のページへ