2021 年度「計算数学b」 2022-01-28

§13.1 ビットごとの演算子 (&, |, ^, <<, >>, ~)

(13.1.1) 英語では bitwise operators と呼ぶ。 加減乗除よりも速いのが特長である。

(13.1.2) コンピュータの中は「0 と 1 だけの世界」などと言われる。 「0 か 1 か」の情報だけを格納できる“もの”――うまく言えない――を 1 ビット (bit) という。 いままで整数を格納してきた int 型変数は、 いま使っている C コンパイラでは、32 個のビットを並べて用い、 二進法で格納している。 よって、格納できる整数は 0 から 232-1 まで、 となりそうだが、およそ半分は負の整数にあてているので、 -231 から 231-1 までである。 負の数をどのように格納するかは、ここでは省略する。

(13.1.3) 簡単のため、下の 8 ビットだけを書いて説明する。 ここでは、0 を偽、1 を真とする。 おのおののビットごとに、「かつ」、「または」、「排他的または」 の計算をおこなう演算子がある。

それらをビットごとに計算するので、次のようになる。

かつ
a10110101
b11010011
a & b10010001
    
または
a10110101
b11010011
a | b11110111
    
排他的または
a10110101
b11010011
a ^ b01100110

C 言語での記号は、上の表の左下のますに書いた。 「^」は、マイナスのキーの右どなりにある。 「かつ」は and, 「または」は or, 「排他的または」は xor と呼ぶこともある。 xor は exclusive or の“略”である。

(13.1.4) ほかに、「左シフト」「右シフト」「否定」の演算子がある。 それぞれ、「<<」、「>>」、「~」 と書く。 「~」は、シフトキーを押しながら「^」のキーを打つ。 「a << 2」は、a のビット列を左に 2 だけずらしたものである。 左にはみ出した 2 ビットは消える。 右には、0 が二つ、はいる。 「a >> 2」は、a のビット列を右に 2 だけずらしたものである。 右にはみ出した 2 ビットは消える。 左には、0 が二つ、はいる。 ただし、a が負であった場合については、ここではふれない。 「~a」は、a のビットをすべて反転したものである。 つまり、0 であったビットは 1 に、1 であったビットは 0 になる。

(13.1.5) 十進で入力した整数を二進で出力するプログラム。 「&」と「>>」を使っている。

#include <stdio.h>

int main() {
  int n, mask;

  printf("0 以上、255 以下の整数を入力してください.\n");
  scanf("%d", &n);

  for (mask = 128; mask != 0; mask = mask >> 1) {
    if ((n & mask) != 0) {
      printf("1");
    } else {
      printf("0");
    }
  }
  printf("\n");
}

注意:「if (n & mask != 0)」と書くと別の意味になる。このへんはややこしい。

128 は、二進の 10000000 である。 この数と「&」をとって 0 でないということは、 下から 8 ビットめが 1 であることを意味する。 この 128 を、「>>」を使って右にシフトしてゆく。

(13.1.6) 二進表記の整数を十進で出力するプログラム。 「|」と「<<」を使っている。

#include <stdio.h>

int main() {
  int n, r, mask;

  printf("二進で正の数を入力してください.\n");
  scanf("%d", &n);

  r = 0;
  mask = 1;
  for (    ; n != 0; n = n/10) {
    if (n % 10 != 0) {
      r = r | mask;
    }
    mask = mask << 1;
  }
  printf("%d\n", r);
}

0 と 1 だけが打ち込まれると想定している。 (0 でない数字は 1 とみなされる。)

mask は 1 から始まって、1 ビットずつ左にシフトしてゆく。 rmask の「|」をとったものを r に代入するということは、 r のそのビットを 1 に変えることを意味する。

§13.2 プログラミングが好きになった人への、挑戦的な問題

(13.2.1) 二つの int 型を引数にとり、それらの和を返す関数 int add(int x, int y) を、 ビットごとの演算子と代入「=」と比較「!=」だけを使って書け。 くり返しの for も使ってよい。

(13.2.2) ヒント:「繰り上がり」を入れておく変数が必要になると思う。 英語では carry と言うらしいので、名前は c でどうだろうか。

(13.2.3) 二つの引数が 0 以上の場合に正しく動くように書けばよい。 その関数に負の数を引数として渡すと、 ここのパソコンでは正しく計算するようである。


岩瀬順一