(『独習C』第13章の理解度チェック大問1)
(コマンドライン引数で与えられた)int型の値の符号を反転した数を返すプログラム:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int n = atoi(argv[1]);
if (!n) {
puts("0");
} else {
int npositive = n;
int shift = 0;
for (; (npositive & 1) == 0; shift++) {
npositive >>= 1;
}
printf("%i\n", ~npositive << shift | 1 << shift);
}
}
(ヒントにある内容を噛み砕く)
まずint型の負数は2の補数。
int型の負数として2の補数を考える。
2の補数は、元の値の最も下の桁の1以下はそのままで、それよりも上の桁のビットが反転したもの。
どういうことかというと、00001100
なら、最も下の桁の1以下とは100
。なのでそれより上の00001
を反転させればよい。
00001101
なら、最も下の桁の1以下とは1
。なのでそれより上の0000110
を反転させればよい。
上記を踏まえてプログラムを見てみる。
まずnpositive & 1
で、一番最初に出てくる1(つまり最も下の桁の1、一番右端にある1)を探し出してる。
ここで& 1
における1
は00000001
のことだから、npositive & 1
は下一桁だけをみてる。
例えば(以下における=
は等号の意味)
00001100 & 00000001 = 00000000 = 0
00000110 & 00000001 = 00000000 = 0
00000011 & 00000001 = 00000001 = 1
この動きのように、npositive >>= 1
で1ビットずつ右シフトしてる(00001100→00000110→00000011)。
最後に「元の値の最も下の桁の1以下はそのままで、それよりも上の桁のビットが反転したもの」を思い出す。
~npositive << shift | 1 << shift
について、そのままにする部分のことを考えて<< shift
してやる。
そしてそれよりも上を反転(~npositive
)してやる。
あとはそれらの和(|
)をとってやれば目的のものが得られる。