ビット演算に関する補足

coskx

1.ビット演算子

ビット演算は整数型変数(char型,int型など)に対して行なわれる演算である。変数のビットパターンに対する演算である。

(1)シフト演算

演算子

意味

<<

左シフト

>>

右シフト

この演算は,整数におけるコンピュータの内部表現において,右や左にずらす演算である。(もともとコンピュータ内部で数値は,2進数で扱われているビットパターンである)
例えば
「12<<3」は12を左に3つずらす意味であり,12はビットパターンで表すと「1100」なので左に3つずらすと「1100000」となりこれは96である。
「12>>2」は12を右に2つずらす意味であり,12はビットパターンで表すと「1100」なので右に2つずらすと「11」となりこれは3である。
一般に整数を左に1つずらすと2倍になり,2つずらすと4倍になり,3つずらすと8倍になり4つずらすと16倍になる。逆に右に1つずらすと1/2倍になり,2つずらすと1/4倍になり,3つずらすと1/8倍になり,4つずらすと1/16倍になる。

int x,y;
の時
y=x/1024;

y=x>>10;
は同じ意味になる。


右ずらしのとき,unsignedで修飾された変数とsignedで修飾された変数で振る舞いが異なる.
 unsignedで修飾された変数 通常の右ずらし
 signedで修飾された変数  最上位ビットの値が保たれる.

unsigned char myvalue=0xf0; のとき
 myvalueは11110000で,myvalue>>3 は00011110になり0x1eになる.
  0xf0は符号なし1バイト整数では240 1/8倍すると30(0x1e)である
signed char myvalue=0xf0; のとき
 myvalueは11110000で,myvalue>>3 は11111110になり0xfeになる.
  0xf0は符号有1バイト整数では-16 1/8倍すると-2(0xfe)であり,つじつまが合っている.

(2)論理演算

演算子

意味

&

ビットごとのand演算

|

ビットごとのor演算

^

ビットごとのxor演算(排他的OR)

~

全ビット反転


例 「157&241」はビットパターンでは「10011101&11110001」となり,以下のように結果は10010001すなわち145となる

1001 1101
& 1111 0001 ビットごとに縦にアンド演算を行う。繰り上がり繰り下がりはない。
-------------
1001 0001

2進法表現

16進法表現

10進法表現

第1変数

1001 1101

9D

157

第2変数

1111 0001

F1

241

結果

1001 0001

91

145

例 「157|241」はビットパターンでは「10011101|11110001」となり,以下のように結果は11111101すなわち253となる

1001 1101
| 1111 0001 ビットごとに縦にオア演算を行う。繰り上がり繰り下がりはない。
-------------
1111 1101

2進法表現

16進法表現

10進法表現

第1変数

1001 1101

9D

157

第2変数

1111 0001

F1

241

結果

1111 1101

FD

253

例 「157^241」はビットパターンでは「10011101^11110001」となり,以下のように結果は01101100すなわち108となる

1001 1101
^ 1111 0001 ビットごとに縦に排他オア演算を行う。繰り上がり繰り下がりはない。
-------------
0110 1100

2進法表現

16進法表現

10進法表現

第1変数

1001 1101

9D

157

第2変数

1111 0001

F1

241

結果

0110 1100

6C

108

例 「~157」はビットパターンでは「~10011101」となり,以下のように結果は01100010すなわち98となる

~ 1001 1101  ビットごとに反転演算を行う。繰り上がり繰り下がりはない。
-------------
0110 0010

2進法表現

16進法表現

10進法表現

第1変数

1001 1101

9D

157

0110 0010

0

62

98

使用例

int k;
のもとで
if (k%256==0) {

if ((k&0xff)==0) {
と同じ意味になる。2のべき乗の時のみ使える

int k,k1,k2,k3;
のもとで

k1=k2=k3=0;
for (k=0; k<10000; k++) {
k1++;
k1&=3;
k2++;
k2&=7;
k3++;
k3&=0xf;
:
:
}
では
k1の値は0,1,2,0,1,2,0,1,2,・・・
のように0,1,2を繰り返し,
k2の値は0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0,1,2,3,・・・
のように0,1,2,・・,7を繰り返し,
k3の値は0,1,2,3,・・,13,14,15,0,1,2,3,・・,13,14,15,0,1,2,3,・・,13,14,15,0,1,2,3,・・
のように0,1,2,・・,13,14,15を繰り返す。

unsigned int x:
のもとで,

if (x&1==1)  はxの第0ビット(右端)が1だったらと意味になる.
if (x&2==2)  はxの第1ビット(右から2番目)が1だったらと意味になる.
if (x&4==4)  はxの第2ビット(右から3番目)が1だったらと意味になる.
if (x&8==8)  はxの第3ビット(右から4番目)が1だったらと意味になる.
if (x&0x10==0x10)  はxの第4ビット(右から5番目)が1だったらと意味になる.

(3)ビット演算子利用上の注意

ビット演算子の優先順位は低いので「()かっこ」を利用しておくとよい。
例 int k1,k2 のもとで
k2=k1<<4+1;
はそのままでは
k2=k1<<(4+1);
のように解釈される。もし
k2=(k1<<4)+1;
の記述をしたければ,そのように書く必要がある。同様に
if (k2==k1&0xff) {
はそのままでは
if ((k2==k1)&0xff) {
のように解釈される。もし
if (k2==(k1&0xff)) {
の記述をしたければ,そのように書く必要がある。

(4)「ビット演算子」+「=」

ビット演算子においても「&=」などの表現ができる。

意味

x <<= 3;

x = x<<3;

x >>= 4;

x = x>>4;

x &= 0xf;

x = x & 0xf;

x |= 0x55;

x = x | 0x55;

x ^= 0xffff;

x = x ^ 0xffff;


2.例1
次の例はunsigned short int型引数の指定したビットが0か1かを返す関数である.
例えば0x5fの第4ビットは(0x5fは101 1111なので)1である.
同様に0x500の第8ビットは(0x500は101 0000 0000なので)1である.
(ビット番号は右から0,1,2,3...と数える)

#include <stdio.h>

int checkBit(unsigned short int value, int bitnumber)
{
    int ret=0;
    int i=0;
    unsigned short int mask=1;
    for (i=0; i<bitnumber; i++) mask<<=1;
    if (value & mask) ret=1;
    return ret;
}

int main()
{
    unsigned short int x;
    int bitnumber;
    int ret;
    x=1;
    bitnumber=0;
    ret=checkBit(x,bitnumber);
    printf("x=%d x=%x bitnumber=%d ret=%d\n",x,x,bitnumber,ret);
    x=0x5f;
    bitnumber=4;
    ret=checkBit(x,bitnumber);
    printf("x=%d x=%x bitnumber=%d ret=%d\n",x,x,bitnumber,ret);
    x=0x500;
    bitnumber=8;
    ret=checkBit(x,bitnumber);
    printf("x=%d x=%x bitnumber=%d ret=%d\n",x,x,bitnumber,ret);
    x=0x500;
    bitnumber=7;
    ret=checkBit(x,bitnumber);
    printf("x=%d x=%x bitnumber=%d ret=%d\n",x,x,bitnumber,ret);
}

/***************************
x=1 x=1 bitnumber=0 ret=1
x=95 x=5f bitnumber=4 ret=1
x=1280 x=500 bitnumber=8 ret=1
x=1280 x=500 bitnumber=7 ret=0
***************************/


3.例2
次の例はunsigned short int型引数の0でない最上位のビット番号を返す関数の例である.
例えば0x5fは1011111なので0でない最上位のビット番号は6である.同様に0x7ffは10である.

#include <stdio.h>

int geNumberBits(unsigned short int value)
{
    int cnt=0;
    int i=0;
    unsigned short int mask=1;
    while (mask) {
        if (value&mask) cnt=i;
        mask<<=1;
        i++;
    }
    return cnt;
}

int main()
{
    unsigned short int x;
    int n;
    x=1;
    n=geNumberBits(x);
    printf("x=%d x=%x n=%d\n",x,x,n);
    x=0x5f;
    n=geNumberBits(x);
    printf("x=%d x=%x n=%d\n",x,x,n);
    x=0x7ff;
    n=geNumberBits(x);
    printf("x=%d x=%x n=%d\n",x,x,n);
}

/***************************
x=1 x=1 n=0
x=95 x=5f n=6
x=2047 x=7ff n=10
***************************/


 


 

課題1.与えたunsigned char型変数の下位2ビットを1にした値を返す関数unsigned char set11(unsigned char value)
を作りなさい.ただし,上位6bitを変更してはならない.
次のmain関数でチェックし,正しい結果になることを確かめなさい.(part0ex1.c)

#include <stdio.h>

unsigned char set11(unsigned char value)
{
    ここを作る
}

int main()
{
    unsigned char x,y;
    for (x=0; x<10; x++) {
        y=set11(x);
        printf("x=%x y=%x\n",x,y);
    }
}

/********実行結果***********
x=0 y=3
x=1 y=3
x=2 y=3
x=3 y=3
x=4 y=7
x=5 y=7
x=6 y=7
x=7 y=7
x=8 y=b
x=9 y=b
***************************/


課題2.与えたunsigned char型変数に1であるビットが何個あるかを返す関数int countOnes(unsigned char value)
を作りなさい.
次のmain関数でチェックし,正しい結果になることを確かめなさい.(part0ex2.c)

#include <stdio.h>

int countOnes(unsigned char value)
{
    ここをつくる
}

int main()
{
    unsigned char x;
    int y;
    for (x=0; x<10; x++) {
        y=countOnes(x);
        printf("x=%x y=%x\n",x,y);
    }
}

/********実行結果***********
x=0 y=0
x=1 y=1
x=2 y=1
x=3 y=2
x=4 y=1
x=5 y=2
x=6 y=2
x=7 y=3
x=8 y=1
x=9 y=2
***************************/