int型の最大値は0xffffffffではない

正体不明のバグが全然取れなくて、ずっと困ってたんですけど、実は処理自体は間違ってなくて、int型の最大値を表現するのに

#define INF (0xffffffff)

みたいなマクロを使ってたせいでした。

実はint型の場合 0xffffffff = -1 です。

どうして-1になるのかは、多分プログラマ的には常識だと思うんですけど、初学者が中途半端に16進数を使うとよく陥りそうな(というか陥った)ミスだったのでメモしておきます。原因が既に分かる人は時間の無駄なのでブラウザバック。

まずint型が取りうる値(今更ですがこのエントリはint型=4byteの環境を想定しています)は、

-2,147,483,648〜2,147,483,648

-2,147,483,648〜2,147,483,647

これは既知。「約20億まで入る」みたいな認識だと思います。

計算機の中で負数を表現する場合、最上位ビットを使って表していて、0なら正数、1なら負数となる。

これも既知。これだけ分かっていれば実は解ける。既知の情報しか使っていないんですよ。だけど思考停止して16進数を使うと痛い目を見るんですよね。

だから正解は0xffffffffではなく0x7fffffff。

もう大体分かったと思うんですけど、ここからちゃんとした解説。

0xffffffffを2進数で表すと、

1111,1111,1111,1111,1111,1111,1111,1111

となり、このとき最上位ビットの値は1です。つまり負数となってしまうんですね。

では正しい最大値はどうなるか。

0111,1111,1111,1111,1111,1111,1111,1111

最上位ビットは0で、それ以外のビットは全て1なので、これが最大値です。

これを16進数で表す場合、

0111 = 0x7

なので0x7fffffffとなります。

ではなぜ 0xffffffff = -2,147,483,648 ではないのか?

感覚的には最上位ビットのみ0のときに20億なら全ビットが1なら-20億になりそうなものですが、ここも単純な理由があります。

例えば、

int a = 0xffffffff + 1;

とすると、aに格納されるのは0x100000000のはずですが、4byteを超えて桁が溢れてしまうため、実際には0x00000000が格納されます。だから-1になっているんですよね。(ただしこの説明はあまり正しくないと思う)

もし0xffffffffが-2,147,483,648だった場合、

int a = -2,147,483,648 + 1; printf(“a=%d”,a);

実行結果はa=0となってしまいます。

逆に 0x7fffffff + 1 (=0x80000000)は、

1000,0000,0000,0000,0000,0000,0000,0000

最上位ビットのみが1なので-2,147,483,648となり、ちゃんと(?)オーバーフローします。

こういうバグ作っちゃうと全然気付かないので、マクロの定義はきちんとしようと思いました(小並感)