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

C言語浮動小数のビット演算は出来ないらしい。

うぅぅぅ。どう求めようか。

浮動小数点の最大値、最小値を計算で求める。

浮動小数ではビット演算が使えないので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の値を越えてしまった・・・。