ビット演算

この記事は約5分で読めます。

C言語にはビット演算という演算方法が用意されています。PCでは全てのデータは0,1の2進法で表現されていて、2進数の一桁を単位としてビットと呼びます。多くの場合1バイト=8ビットなので、例えば、intが4バイトの処理系ではintは32ビットとして扱われています。

シフト演算とは

C言語のビット演算とは、ビットを右または左にずらす演算です。このずらす演算をシフトといいます。

<<演算子を使用すると、左方向にビットをシフトできます。

a<<b

aをbビット左にシフトします。空いた右側のビットは0となります。

反対に、>>演算子を使用すると右方向にビットをシフトできます。

a>>b

aをbビット右にシフトします。

どちらの演算子も、シフトした後の値を生成するのであって、aの値が変更されるわけではありません。

#include <stdio.h>

int main(void) {
	int a=10;
	printf("a=%d\na<<1=%d\na<<2=%d\n",a,a<<1,a<<2);
	printf("a=%d\na>>1=%d\na>>2=%d\n",a,a>>1,a>>2);
	return 0;
}

実行結果

a=10
a<<1=20
a<<2=40
a=10
a>>1=5
a>>2=2

ビット演算子を使用すると数値が2進法表記において末尾に0が追加されるか、末尾の桁が削除されます。つまり、1ビット左にシフトすると値が2倍になり、1ビット右にシフトすると値が2分の1になります。小数点以下は切り捨てられます。2ビットシフトするとさらに2倍、2分の1倍になります。

ビットごとに表示

シフト演算子の性質より、数値を2進法で表示することができます。

#include <stdio.h>

void inttobits(int n){
	int i;
	printf("%d=",n);
	for (i=8*sizeof(int)-1;i>=0;i--){
		printf("%d",(n>>i)%2?1:0);
	}
	printf("\n");
}

int main(void) {
	int i;
	for(i=-9;i<=9;i++){
		inttobits(i);
	}
	return 0;
}

実行結果

-9=11111111111111111111111111110111
-8=11111111111111111111111111111000
-7=11111111111111111111111111111001
-6=11111111111111111111111111111010
-5=11111111111111111111111111111011
-4=11111111111111111111111111111100
-3=11111111111111111111111111111101
-2=11111111111111111111111111111110
-1=11111111111111111111111111111111
0=00000000000000000000000000000000
1=00000000000000000000000000000001
2=00000000000000000000000000000010
3=00000000000000000000000000000011
4=00000000000000000000000000000100
5=00000000000000000000000000000101
6=00000000000000000000000000000110
7=00000000000000000000000000000111
8=00000000000000000000000000001000
9=00000000000000000000000000001001
(実行結果は環境により異なります)

inttobits関数では、intのビット数だけループし、そこではiビット右にシフトし、一の位が0か1かで表示する数値を変えています。左から、大きい位から表示するためforループでiの数を1ずつ減らしています。

この結果を見るとわかるように、正の数は右にシフトすると空いたビットに0が詰められますが、負の数では1が詰められます。

実は、正の数は右にシフトすると0が詰められるように決められていますが、負の数は0が詰められる場合と1が詰められる場合があります。これは処理系や環境によって異なっています。

論理シフトと算術シフト

負の数を右にシフトするとき、論理シフトと算術シフトの2つの方法があり、どちらで実装されているかは処理系によって異なります。

論理シフト

符号ビットを特別に考慮することなく、シフトします。空いたビットは0になるので、符号ビットが1から0に変わりシフトの結果負の値が正になります。

算術シフト

正常位の符号ビットを保存しそれ以外のビットのみをシフトします。つまり、負の数を右にシフトすると空いたビットは1が詰められます。符号が保存されるだけでなく1ビット左シフトすると値が2倍になり1ビット右シフトすると値が1/2になるという関係が保たれます。

コメント