K&Rを読もう(22) 演習2-1 変数の範囲
演習2-1
変数の範囲を調べる問題。
面倒なのでマクロで自動生成してみた。
#include <stdio.h> #include <stdlib.h> #include <limits.h> #include <float.h> #define UCHAR_MIN 0 #define USHRT_MIN 0 #define UINT_MIN 0 #define size(s, t, S) printf("%-15s : %2d byte %2d bit %16" #t "%16" #t "\n", \ #s, sizeof (s), sizeof (s) * 8, S ## _MIN, S ## _MAX) int main(void) { size(unsigned char, d, UCHAR); size(char, d, CHAR); size(unsigned short, d, USHRT); size(short, d, SHRT); size(unsigned int, u, UINT); size(int, d, INT); size(float, e, FLT); size(double, e, DBL); exit(EXIT_SUCCESS); }
メモ。
- "hoge" "moge"で"hogemoge"という文字列が作れる。
- #hogeで、マクロの文字列展開が出来る。
- INT ## _MAXから INT_MAXの多段マクロを生成出来る。##した場合は再スキャンが行われる。
実行結果
% ./ex-2-1 unsigned char : 1 byte 8 bit 0 255 char : 1 byte 8 bit -128 127 unsigned short : 2 byte 16 bit 0 65535 short : 2 byte 16 bit -32768 32767 unsigned int : 4 byte 32 bit 0 4294967295 int : 4 byte 32 bit -2147483648 2147483647 float : 4 byte 32 bit 1.175494e-38 3.402823e+38 double : 8 byte 64 bit 2.225074e-308 1.797693e+308
いい感じ。
gcc -Eでマクロを展開すると大変な事になる。
int main(void) { printf("%-15s : %2d byte %2d bit %16" "d" "%16" "d" "\n", "unsigned char", sizeof (unsigned char), sizeof (unsigned char) * 8, 0, (127 * 2 + 1)); printf("%-15s : %2d byte %2d bit %16" "d" "%16" "d" "\n", "char", sizeof (char), sizeof (char) * 8, (-127 - 1), 127); ..(略
使いどころを考えればマクロが有効な事もある。
計算で求める
計算で求める場合は、ビット演算が使える。
printf("UINT_MAX %u\n", ~0); printf("INT_MAX %d\n", ~(1 << sizeof (int) * 8 - 1)); printf("INT_MIN %d\n", 1 << sizeof (int) * 8 - 1);
~は補数を取る。ビットを逆に出来る。
~1111 = 0000 ~0000 = 1111
1をビットシフトさせればINT_MIN。補数を取れば、INT_MAX。
4ビットsignedで表わすと、
0001 << 3 = 1000 = -7 ~(0001 << 3) = 0111 = 7
ビットローテートを使いたい所なんですが、C言語にビットローテートがない!!
実行結果。
UINT_MAX 4294967295 INT_MAX 2147483647 INT_MIN -2147483648
うぅぅぅ。どう求めようか。
浮動小数点の最大値、最小値を計算で求める。
浮動小数ではビット演算が使えないのでldexpを使って求めた。
ldexp(x,n)は x * 2^nなので、
- 2の1023乗を求めるときは、ldexp(1,1023)
- 2の-1023乗を求めるときは、ldexp(1,-1023)
となる。2の1023乗ってどんな大きさなのか想像が出来ないけど、ものすごくでかそう。
ライブラリ内部的ではビット演算してるだけだと思う。
では、DBL_MAXを求めてみよう。
int i; double x = 0; for(i = 1023; i > 970; i--) x += ldexp(1,i); printf("DBL_MAX %e\n", x); // DBL_MAX 1.797693e+308
なんとかDBL_MAXが出せた。
2^1023 + 2^1022 + 2^1021 + ....
として、ビットのフラグを一本づつ立てていく。i >= 0 までやると、オーバーフローする。
DBL_MINを求める。
printf("DBL_MIN %e\n", ldexp(1, -1074)); // DBL_MIN 4.940656e-324
アレ?DBL_MINの値を越えてしまった・・・。