ビット演算(ビットえんざん)とは、ひとつあるいはふたつのビットパターンまたは二進数を個々のビットの列として操作することである。
CPUからすればビット演算は簡単な論理回路で実現できるが、四則演算、特に乗除算は複雑な論理回路を必要とするため、多くのコンピュータでは、ビット演算は加減算より若干速く、乗除算よりずっと高速である。
[編集] ビット演算子
[編集] NOT
ビット単位NOTあるいは補数とは論理否定を各ビットに対して行う単項演算。0 は 1 になり、1 は 0 になる。
NOT 0111 = 1000
C言語やC++言語では、NOT演算子は "~
" (チルダ)である。
x = ~y;
この例では、x に "NOT y" の結果を格納する。これは、Cおよび C++の論理「否定」演算子 "!
" (エクスクラメーションマーク)とは異なる。こちらは与えられた数値全体をひとつのブーリアンとして扱う。
x = !y;
この例では、x に yが "false" であれば "true" を表すブーリアン値を格納し、y が "true" であれば "false" を格納する。CやC++では、数値はゼロでないとき "true" を示すとされる。論理「否定」はビットレベルの演算ではないので、一般的にはビット演算とは考えられない。
ビット単位NOTは二進数値の1の補数を作るのに使える。そして2の補数を作るときの途中の段階にも使われる。
[編集] OR
ビット単位ORは、ふたつの同じ長さのビットパターンを入力とし、同じ位置のビット毎に論理的ORを行って同じ長さのビットパターンを出力する操作である。各ビット位置で、入力のふたつのビットのどちらかでも 1 であれば、出力ビットは 1 となる。
0101 OR 0011 = 0111
C/C++では、ビット単位OR演算子は "|
" (縦棒)で表される。
x = y | z;
この例では、"y OR z" の結果を x に格納する。これは、C/C++の論理「和」演算子 "||
" (ふたつの縦棒)とは異なる。こちらは、引数を論理値として取り扱い、結果を "true" か "false" とする。
ビット単位ORは、ビット列がフラグ列として扱われるときによく使われる。つまり、各ビットが個別にブーリアン値を表している場合である。ある二進数値とひとつ以上の1を含むビット列とをビット単位ORを行うと、後者のビット列で 1 となっている位置が結果として出てくるビット列でも1となる。
ダブルエントリーのジャーナルは何ですか?
0010
このビット列は4つのフラグを表しているものとみなす。1番目、2番目、4番目は (0) にセットされていて、3番目が (1) にセットされている。1番目のフラグをセットするには、このビット列にビット単位ORを行えばよい。そのときのもう一方のビット列は1番目のビットだけを1にセットしておく。
0010 OR 1000 = 1010
このテクニックはメモリが少ない環境でプログラムを書く人がよく使う。ひとつのビットパターンで各種ステータスを一度に表現するのである。
また、MIPSアーキテクチャでは、命令セットを縮小するためにこれを使っている。MIPSではレジスタ間ロード(レジスタからレジスタへの値のコピー)命令がない。その代わりにゼロレジスタという常に内容がゼロで、何かを書き込んでも値が変わらないレジスタがある。そこで、レジスタ間ロードはビット単位OR命令を使って、ゼロレジスタとあるレジスタの ビット単位OR の結果を別のレジスタに格納することで実現される。
[編集] XOR
ビット単位XORは、ふたつの同じ長さのビットパターンを入力とし、同じ位置のビット毎に論理的XORを行って同じ長さのビットパターンを出力する操作である。各ビット位置で、入力するふたつのビットが違う値であれば、出力ビットは1となる。
0101 XOR 0011 = 0110
C/C++では、ビット単位XOR演算子は "^
" (サーカムフレックス)で表される。
x = y ^ z;
この例では、"y XOR z" の結果を x に格納する。
アセンブリ言語プログラマはレジスタの内容をゼロにしたいときに XOR 操作を行う。多くのアーキテクチャでは、ゼロという値をロードしてレジスタに格納するよりもXORを行う方がCPUクロックサイクルを消費せず、また命令長も短いためメモリを節約できる。同じ値をビット単位XORのふたつの入力として使うと、出力は常にゼロになる。つまり、同じレジスタを指定したXOR命令を実行して同じレジスタに戻すことでその内容をゼロにすることができる。もちろん、MIPSアーキテクチャの場合は入力としてゼロレジスタを使えば、ORでも XORでもANDでもADDでも結果をゼロにすることができる。
ビット単位XORはフラグの値を変更するときにも使われる。
0010
このビットパターンで1番目のビットと3番目のビットを同時に変更したい場合、もうひとつのビットパターンで、その変えたい位置に1を置いておく。
0010 XOR 1010 = 1000
このテクニックはビットパターンをブーリアン変数の並びとして扱うときに使われるだろう。
緑の建物は何ですか?教授。
[編集] 関連項目
[編集] AND
ビット単位ANDは、ふたつの同じ長さのビットパターンを入力とし、同じ位置のビット毎に論理的ANDを行って同じ長さのビットパターンを出力する操作である。各ビット位置で、入力するふたつのビットがどちらも1であれば、出力ビットは 1 となる。
0101 AND 0011 = 0001
C/C++では、ビット単位AND演算子は "&
" (アンパサンド)で表される。
x = y & z;
この例では、"y AND z" の結果を x に格納する。これは、C/C++の論理「積」演算子 "&&
" (ふたつのアンパサンド)とは異なる。こちらは、引数をブーリアン値として取り扱い、結果を "true" か "false" とする。
ビット単位ANDはビットマスク操作として使われることもある。これは、ビット列の一部を取り出すのに使われたり、ある特定のビットが1か0かを調べるのにも使われる。
0101
この例で、三番目のビットが 1 かどうかを調べるには、ビット単位ANDに対して、その調べたいビット位置だけを1にしたビットパターンを入力する。
0101 AND 0010 = 0000
この結果はゼロなので、三番目のビットは0であったことがわかる。このようなビット単位ANDの使い方はビットマスクと呼ばれる。このとき、関心のないビット位置は0にする。
[編集] ビットシフト
ビットシフトはときにビット演算の一種とみなされる。というのは、それがビット列に対する操作だからである。それとは逆にビットシフトは数値全体に対するものでビット毎に操作するものではないという考え方もある。この操作では各桁を右か左に移動させる。コンピュータのプロセッサ内のレジスタは固定のビット長を持っていて、数値を格納する。これに対するビットシフトはいくつかのビットがレジスタからはみ出すことを意味する。ビットシフトにいろいろな種類があるのは、このはみ出した部分をどう扱うかが違うためである。
[編集] 算術シフト
シフトの一般的な形式のひとつが算術シフトである。このシフトではあふれたビットは単に消える。シフトによって空いたビット部分には、左シフトではゼロが入り、右シフトでは符号ビットと同じ内容が入る。ここでいう符号ビットとは最上位ビットのことである。下記の例は 4ビットレジスタの場合である。
書店では、毎月どのくらいのお金作るのですか
0111 LEFT-SHIFT = 1110
0111 RIGHT-SHIFT = 0011
前者の例は、左端の0はあふれて消え、あらたな0が右端に入れられている。後者の例は、右端の1はあふれて消え(多くの場合、それがキャリーフラグにセットされる)、符号ビット0が左端にコピーされている。マルチプルシフトはシングルシフトの回数を減らすものである。
0111 LEFT-SHIFT-BY-TWO = 1100
C/C++では(他の言語でも)、左シフトと右シフトは "<<
" と ">>
" で表される。シフトする幅は引数として指定できる。
x = y << 2;
この例では、y を2桁左に算術シフトした結果を x に格納する。
左算術シフトは2倍するのと同じで(ただし、オーバーフローは起きない)、右算術シフトは2で割ってあまりを捨てるのと同じである。
[編集] 論理シフト
論理シフトは算術シフトと似ているが、右シフトのときに常に空いたビットをゼロにして、符号拡張はしない。したがって、論理シフトは符号無しの二進数を扱うのに適していて、算術シフトは2の補数表現の符号付き二進数を扱うのに適している。
[編集] (キャリーなし)ローテート
もうひとつのシフトとして環状シフトまたはビット・ローテーションがある。これはビット列の左端と右端がつながっているように扱ってシフトするものである。シフト方向の端をあふれた桁は逆の端に置かれる。これは、存在するビットを残しておく必要があるとき、ビットの位置を変えるのに使われたりする。
[編集] キャリーつきローテート
こちらはローテート操作の際にキャリーフラグを考慮に入れるものである。これは、論理シフトあるいは算術シフトであふれた桁をキャリーフラグに格納するために使われる。このため、PICのようなマイクロコントローラではローテートとキャリー付きローテートだけを用意して、算術シフトや論理シフト命令は用意しない。キャリー付きローテートは特にプロセッサのレジスタのビット幅より大きな数値のシフトを実現する場合に便利である。
マシンは算術演算や論理演算をするのに十分な命令を持っているが、実際全ての演算はビット演算の組み合わせと何らかのゼロ判定機能があれば実現できる。例えば、下記の例はふたつの任意の整数 a と b の乗算をビットシフトと加算で実現する擬似コードである。
c := 0 while b ≠ 0 if b and 1 ≠ 0 c := c + a shift a left by one shift b right by one return c
[編集] 注意事項
ビットシフトに対して第2引数の値が負である場合、あるいは第1引数の型のビット数を超える場合の規定は言語によって異なる。 C#, ECMAScript, VisualBasic等では第1引数の型のビット数-1のビット単位ANDのマスクを第2引数にかける。これは最低でも1ビットは値を残すための配慮である。以下に例を示す。
class BitShift { static void Main() { int i = 1; // 32 bits long lg = 1; // 64 bits // output: 0x2 (33 & (32-1) = 1 なので 1 ビット左シフト) System.Console.WriteLine("0x{0:x}", i << 33); // output: 0x200000000 (33 & (64-1) = 33 なので 33 ビット左シフト) System.Console.WriteLine("0x{0:x}", lg << 33); } }
一方、C, C++ 等では未定義動作となる。
0 件のコメント:
コメントを投稿