ゆうなんとかさんの雑記帳的な。

Twitterで踊ったり音ゲーしたりしてるあの名前がよくわからない人が書いてるらしいよ。

ビット演算まとめ

私が研究で書くようなコードではあんまり使わないのですが、ある種のたしなみとしてちょっとメモしておきます。>>と<<がどうなるのかよく度忘れするので…

ビット演算とは

読んで字のごとく、ビットを直接操作する演算のことです。足し算よりもほんの少し早く、乗算よりもすごく早いといわれています。また、うまく使うとひとつの変数に複数の役割を持たせるような使い方もできます。いくつかあるフラグをまとめて立てたりおろしたりを1回の演算ですることもできます。

ビット演算の種類

というわけでみていきます。例に載せているのはC#です。

否定演算

ビットを反転させる単項演算です。1を与えれば0、10というビット列を与えれば01がかえってきます。

byte x = 2;  //0000 0010(2)
byte y = ~x; //1111 1101(2)
Console.Write(y); //253

AND演算

ふたつのビット列を見比べて、両方が1の位を1、そうでなければ0を返す二項演算です。&&とは違いますよ?

byte x = 165;   //1010 0000(2)
byte y = 46;    //0010 1110(2)
byte z = x & y; //0010 0000(2)
Console.Write(z); //32

OR演算

ふたつのビット列を見比べて、どちらかが1の位を1、そうでなければ0を返す二項演算です。こちらも||とは違います。

byte x = 165;   //1010 0000(2)
byte y = 46;    //0010 1110(2)
byte z = x | y; //1010 1110(2)
Console.Write(z); //174

XOR演算

ふたつのビット列について、どちらかのみ1の位を1、そうでなければ0を返す二項演算です。XOR(Exclusive OR:排他的論理和)はうまく使うと剰余並に便利そうです。

byte x = 165;   //1010 0000(2)
byte y = 46;    //0010 1110(2)
byte z = x ^ y; //1000 1110(2)
Console.Write(z); //142

シフト演算

これはひとつの変数をビットの配列とみなして、右や左に動かすという演算です。2のn乗倍が高速にできたり、複数のビット列を重ね合わせてひとつのビット列にしたりするときに使うんじゃないでしょうかね?算術シフト*1、論理シフト*2、ローテーションシフト*3など、シフトのされ方によって挙動が異なるので、(私も含め)よくわからない人は符号なし整数で繰り越しや切り捨てが起きて泣かない程度に使っておきましょう。

右シフト

ビット列を右にシフトします。これを符号なし整数型の変数にすると、シフトした分だけ2で割って、余りは切り捨てたことになります。あふれた分は切り捨てられるか、頭のビットに回されるかのどちらかになります。

byte x = 2;       //0000 0010(2)
byte y = x >> 1;  //0000 0001(2)
Console.Write(y); //1

左シフト

ビット列を左にシフトします。これを符号なし整数型の変数にすると、シフトした分だけ2をかけたことになります。あふれた分はたいていなかったことになります。

byte x = 2;       //0000 0010(2)
byte y = x << 1;  //0000 0100(2)
Console.Write(y); //4

なんで今更これなのか

文字解析しているときに、ある1文字を表すByte型の配列を、ひとつの数値に変換してあれこれしたくなったからです。これは明日のこととも絡んでくるのですが…

*1:符号ありの変数であれば符号ビットが保持される。右シフトのときはあふれたビットが最上桁のビットにつっこまれるが、左シフトの場合は最下位ビットには常に0が補填される

*2:符号ビットが保持されないのと、右シフトのときも単に0が補填される以外は算術シフトと同様

*3:ビット列が円環になっているかのようにふるまう