浮動小数点の精度の確認

C言語における浮動小数点の精度を確認してみた。検証した環境は、

プロセッサ Intel Core2 Duo
OS Ubuntu 8.04 AMD64
コンパイラ gcc version 4.2.3

(OSはVMwareの仮想環境で、ホストOSはFedora 8 x64)

この環境で次のプログラムを実行した。

#include <stdio.h>
#include <math.h>
#include <float.h>

main()
{
        float           f;
        double          d;
        long double     l;

        printf("sizeof(float) = %d bytes (%d bits)\n", sizeof(f), sizeof(f)*8);
        printf("float\t\t%d\t%d\t%d\t%d\t%E\t%E\n",
                FLT_RADIX, FLT_MIN_EXP, FLT_MAX_EXP, FLT_MANT_DIG, FLT_MIN, FLT_MAX);

        printf("sizeof(doule) = %d bytes (%d bits)\n", sizeof(d), sizeof(d)*8);
        printf("double\t\t%d\t%d\t%d\t%d\t%E\t%E\n",
                FLT_RADIX, DBL_MIN_EXP, DBL_MAX_EXP, DBL_MANT_DIG, DBL_MIN, DBL_MAX);


        printf("sizeof(long doule) = %d bytes (%d bits)\n", sizeof(l), sizeof(l)*8);
        printf("long double\t%d\t%d\t%d\t%d\t%LE\t%LE\n",
                FLT_RADIX, LDBL_MIN_EXP, LDBL_MAX_EXP, LDBL_MANT_DIG, LDBL_MIN, LDBL_MAX);

        printf("\n");

        // 3.14159 26535 89793 23846 26433 83279 50288
        f = 3.1415926535897932384626433832795028;
        printf("f = 3.1415926535897932384626433832795028;\t= %.34E\n", f);
        d = 3.1415926535897932384626433832795028;
        printf("d = 3.1415926535897932384626433832795028;\t= %.34E\n", d);
        d = 3.1415926535897932384626433832795028l;
        printf("d = 3.1415926535897932384626433832795028l;\t= %.34E\n", d);
        l = 3.1415926535897932384626433832795028;
        printf("l = 3.1415926535897932384626433832795028;\t= %.34LE\n", l);
        l = 3.1415926535897932384626433832795028l;
        printf("l = 3.1415926535897932384626433832795028l;\t= %.34LE\n", l);
        printf("\t\t\t\t\t\t  3.1415926535897932384626433832795028\n");
}

実行結果は以下の通り。

sizeof(float) = 4 bytes (32 bits)
float		2	-125	128	24	1.175494E-38	3.402823E+38
sizeof(doule) = 8 bytes (64 bits)
double		2	-1021	1024	53	2.225074E-308	1.797693E+308
sizeof(long doule) = 16 bytes (128 bits)
long double	2	-16381	16384	64	3.362103E-4932	1.189731E+4932

f = 3.1415926535897932384626433832795028;	= 3.1415927410125732421875000000000000E+00
d = 3.1415926535897932384626433832795028;	= 3.1415926535897931159979634685441852E+00
d = 3.1415926535897932384626433832795028l;	= 3.1415926535897931159979634685441852E+00
l = 3.1415926535897932384626433832795028;	= 3.1415926535897931159979634685441852E+00
l = 3.1415926535897932384626433832795028l;	= 3.1415926535897932385128089594061862E+00
					  3.1415926535897932384626433832795028

以下、考察。

4倍精度の実装が寂しい

4倍精度の仮数部の64ビットしか無い。フル実装であれば112ビットであるはずだが。4倍精度では変数として16バイトを占有するが実際に使わるのは64ビット+16ビット=10バイトだけということになる。IEEEの定義では64ビット(8バイト)を超えた実装は全て4倍精度と呼べるらしいので、規格には合っているようだが。これにより、本来4倍精度では仮数部の精度が10進33桁ある筈だが、19桁程しかない。倍精度の15桁よりも僅か多いだけ。それで、8バイトも余計に使うのは投資対効果に見合うか? さて、どうしたものか。
仮数部が64ビットしかないのはIntel Core2アーキテクチャの影響か?(未確認)

4倍精度の定数には“l”が必要

変数を4倍精度で定義しても式の中で定数を使う場合、“l”をつけないと倍精度扱いになってしまい、仮数部の精度が落ちる。