浮動小数点数の詳細
文字列化する関数を書くんでもない限り別に知らなくてもいいんだけど
float でも double でも別に指数部と仮数部のビット幅が違うだけでその解釈は同じです
MSB.............................LSB | ||
S | EXPONENT | MANTISSA |
符号 | 指数部 | 仮数部 |
浮動小数点数は基本的には次のように評価されます
±1.仮数部×2指数部
== ±( 2指数部 + 仮数部の最上位ビット*2指数部-1 +...+ 仮数部の最下位ビット*2指数部-仮数部のビット数 )
仮数部
仮数部は最上位ビットが 2-1(==0.5)で最下位ビットが 2-仮数部のビット数 になります
正規数(普通の値)では最上位ビットの上に暗黙の 20(==1)を仮定します
例えば仮数部が 23ビットである IEEE 754 単精度 float で仮数部が 10100000000000000000000 のとき、この仮数部の値自体は 2-1 + 2-3 で 0.675 になります
10100000000000000000000
== 1*2-1 + 0*2-2 + 1*2-3 + 0*2-4 ...
== 2-1 + 2-3
== 0.5 + 0.125
== 0.675
正規数(普通の値)ではさらに暗黙の 20(==1)があるのでこれを足して仮数部は実際には 1.675 と評価されることになります
1.10100000000000000000000
== 1*20 + ( 1*2-1 + 0*2-2 + 1*2-3 + 0*2-4 ... )
== 20 + ( 2-1 + 2-3 )
== 1.0 + ( 0.5 + 0.125 )
== 1.675
非正規数ではこの暗黙の 20(==1)は仮定されません
指数部
指数部は2の補数表現ではなくバイアス/エクセス表現になっています
上半分が正の数、真ん中が 0、下半分が負の数となります
最小値(全ビット 0 )と最大値(全ビット 1 )は特別扱いになっています
指数部が最大値(全ビット 1 )のときは NaN か ±∞ になります
指数部が最小値(全ビット 0 )のときは 0 か非正規数になります
指数部が 8 ビットである IEEE 754 単精度 float では次のように解釈されます
11111111 | NaN または ∞ | |
11111110 | 254-127 | +127 |
11111101 | 253-127 | +126 |
11111100 | 252-127 | +125 |
. | . | . |
. | . | . |
. | . | . |
10000010 | 130-127 | +3 |
10000001 | 129-127 | +2 |
10000000 | 128-127 | +1 |
01111111 | 127-127 | 0 |
01111110 | 126-127 | -1 |
01111101 | 125-127 | -2 |
01111100 | 124-127 | -3 |
. | . | . |
. | . | . |
. | . | . |
00000003 | 3-127 | -124 |
00000002 | 2-127 | -125 |
00000001 | 1-127 | -126 |
00000000 | 非正規数 または 0 |
普通の数
普通の数である正規数は次のように評価されます
±1.仮数部×2指数部
== ±( 2指数部 + 仮数部の最上位ビット*2指数部-1 +...+ 仮数部の最下位ビット*2指数部-仮数部のビット数 )
正規数の最大値は指数部の最下位ビット以外 1 で最下位ビットが 0 (指数部の最大値-1)で仮数部は全ビット 1
正規数の最小値は指数部の最下位ビット以外 0 で最下位ビットが 1 (指数部の最小値+1)で仮数部は全ビット 0
最大値
x | 1.......10 | 1.................1 |
最小値
x | 0.......01 | 0.................0 |
FLT_MIN, FLT_MAX, DBL_MIN, DBL_MAX などがこのビットパターンになっています
特別な数
∞
指数部が全ビット 1 で仮数部が 0 のときは ∞
符号ビットによって ±∞
+∞
0 | 1........1 | 0.................0 |
-∞
1 | 1........1 | 0.................0 |
NaN
指数部が全ビット 1 で仮数部が 0 以外のときは NaN
Intel の場合はさらに仮数部の最上位ビットが 1 かどうかで QNaN と SNaN に分かれる
仮数部の最上位ビットが 1 だったら QNaN・・・だったかな?
SNaN
x | 1........1 | 0x................x |
QNaN
x | 1........1 | 1x................x |
指数部と仮数部が共に 0 のときは 0
符号ビットによって ±0
0
0 | 0........0 | 0.................0 |
-0
1 | 0........0 | 0.................0 |
負の 0 と 0 のビットパターンは異なりますが、C/C++ で負の 0 と 0 を浮動小数点数として区別することはできません
( 0.0f == -0.0f ) → TRUE
( 0.0f <= -0.0f ) → TRUE
( 0.0f >= -0.0f ) → TRUE
( 0.0f != -0.0f ) → FALSE
( 0.0f < -0.0f ) → FALSE
( 0.0f > -0.0f ) → FALSE
Visual Studio 2008 より前の Visual C++ では -0.0 と書いても負の 0 とは評価されないので気をつけましょう
例えば次のように -0.0f を代入しても f0n は 0.0f(==0x00000000)で初期化されます
float f0n = -0.0f;
非正規数
指数部が 0 で仮数部が 0 以外のときは非正規数
x | 0........0 | x.................x |
非正規数は次のように評価されます
±0.仮数部×2指数部
== ±( 仮数部の最上位ビット*2指数部-1 +...+ 仮数部の最下位ビット*2指数部-仮数部のビット数 )
非正規数では正規数と違い仮数部に暗黙の 20 を仮定しません
また非正規数の指数部は 0 ですが 1 のときと同じ値として評価されます
例えば指数部が 8 ビットである IEEE 754 単精度 float では非正規数の指数部は -126 であるとみなされますから値全体としては
±0.仮数部×2-126
== ±( 仮数部の最上位ビット*2-126-1 +...+ 仮数部の最下位ビット*2-126-仮数部のビット数 )
と評価されます
これらのことから非正規数は 0.0 〜 正規数の最小値までの精度を向上させます
大きさの関係はこんな感じになります
0.0 < 非正規数の中の最小値 < 非正規数の中の最大値 < 正規数の最小値
0.0
x | 0.......00 | 0................00 |
非正規数の中で最も小さい値
x | 0.......00 | 0................01 |
非正規数の中で最も大きい値
x | 0.......00 | 1.................1 |
正規数の最小値
x | 0.......01 | 0.................0 |
Epsilon
Epsilon は 1.0 より大きい最小の正規数と 1.0 との差です
1.0 は 20 なので仮数部は 0 です
20
0 | 01.....1 | 0.................0 |
これより大きい最小の正規数は仮数部に 1 を足したものですね
20 + 20-仮数部のビット数
0 | 01.....1 | 0................01 |
20 + 20-仮数部のビット数 と 20 の差なのですから、つまり Epsilon は 2-仮数部のビット数 です
例えば仮数部が 23ビットである IEEE 754 単精度 float では Epsilon は 2-23 になります
2-23 は 2 のべき乗なので仮数部は 0 になります。指数部は float のバイアス値である 127 を足して -23+127 で 104 = 0x68 = 0110 1000 になります
FLT_EPSILON
0 | 01101000 | 0.................0 |
1.0f + FLT_EPSILON
0 | 01111111 | 0................01 |
1.0f
0 | 01111111 | 0.................0 |
仮数部が 52ビットである IEEE 754 倍精度 double では Epsilon は 2-52 になります
2-52 は 2 のべき乗なので仮数部は 0 になります。指数部は double のバイアス値である 1023 を足して -52+1023 で 971 = 0x3CB = 011 1100 1011 になります
DBL_EPSILON
0 | 01111001011 | 0.................0 |
1.0 + DBL_EPSILON
0 | 01111111111 | 0................01 |
1.0
0 | 01111111111 | 0.................0 |
ところで、この Epsilon という値は「ある基準値とその基準値よりも大きい最小の正規数との差」ですから、基準が 1.0 でない一般の場合で言うと 2n と 2n + 2n-仮数部のビット数 の差ということになります
このとき精度としては 2-仮数部のビット数 ということで変わりありませんが、値としては 2n-仮数部のビット数 ですから、例えば仮数部が 23ビットである IEEE754 単精度 float でこの基準を 256.0f に置いてみると、256.0f つまり 28 に対する Epsilon は 28-23 で 2-15 であるということになります
2-15
0 | 01110000 | 0.................0 |
256.0f + 2-15
0 | 10000111 | 0................01 |
256.0f
0 | 10000111 | 0.................0 |
ビットパターンを見てもお判りの通り 256.0f と 256.0f + 2-15 の差は 1 しかありませんから、この2つの数の間には表現可能な数は存在しません
従って 256.0f に 2-15 より小さい値を足しても意味がありません
× 256.0f に FLT_EPSILON ( 2-23 ) を足しても意味が無い
float f = sample_distance( foo, bar ); if( f <= 256.0f + FLT_EPSILON ){ . . . }
具体的には
具体的にはこんな感じ
fp32(s23e8) - IEEE 754 単精度 float
#define IEEE754_FLOAT_SIGN_BITS 1 #define IEEE754_FLOAT_EXPONENT_BITS 8 #define IEEE754_FLOAT_MANTISSA_BITS 23 #define IEEE754_FLOAT_SIGN_MASK (0x80000000UL) #define IEEE754_FLOAT_EXPONENT_MASK (0x7f800000UL) #define IEEE754_FLOAT_MANTISSA_MASK (0x007fffffUL) #define IEEE754_FLOAT_QNAN (0x00400000UL) #define IEEE754_FLOAT_POSITIVE_INFINITE (0x7f800000UL) #define IEEE754_FLOAT_NEGATIVE_INFINITE (0xff800000UL) #define IEEE754_FLOAT_POSITIVE_ZERO (0x00000000UL) #define IEEE754_FLOAT_NEGATIVE_ZERO (0x80000000UL) #define IEEE754_FLOAT_MAX (0x7f7fffffUL) // < 2<sup>+128</sup> #define IEEE754_FLOAT_MIN (0x00800000UL) // == 2<sup>-126</sup> #define IEEE754_FLOAT_EPSILON (0x34000000UL) // == 2<sup>-23</sup> #define IEEE754_FLOAT_ISMINUS( fl ) ((fl) & IEEE754_FLOAT_SIGN_MASK) #define IEEE754_FLOAT_ISZERO( fl ) ( !((fl) & ~IEEE754_FLOAT_SIGN_MASK)) #define IEEE754_FLOAT_FINITE( fl ) (((fl) & IEEE754_FLOAT_EXPONENT_MASK)!=IEEE754_FLOAT_EXPONENT_MASK) #define IEEE754_FLOAT_NORMALIZED( fl ) ((((fl) & IEEE754_FLOAT_EXPONENT_MASK)!=IEEE754_FLOAT_EXPONENT_MASK)&& ((fl) & IEEE754_FLOAT_EXPONENT_MASK)) #define IEEE754_FLOAT_ISNAN( fl ) ((((fl) & IEEE754_FLOAT_EXPONENT_MASK)==IEEE754_FLOAT_EXPONENT_MASK)&& ((fl) & IEEE754_FLOAT_MANTISSA_MASK)) #define IEEE754_FLOAT_SIGN( fl ) (((fl) >> (IEEE754_FLOAT_EXPONENT_BITS+IEEE754_FLOAT_MANTISSA_BITS)) & 0x00000001UL) #define IEEE754_FLOAT_EXPONENT( fl ) ( !IEEE754_FLOAT_NORMALIZED((fl)) ? (IEEE754_FLOAT_EXPONENT_MIN+1) : ((((fl) & IEEE754_FLOAT_EXPONENT_MASK)>>IEEE754_FLOAT_MANTISSA_BITS)+IEEE754_FLOAT_EXPONENT_MIN)) #define IEEE754_FLOAT_MANTISSA( fl ) ( !IEEE754_FLOAT_NORMALIZED((fl)) ? ((fl) & IEEE754_FLOAT_MANTISSA_MASK) : (((fl) & IEEE754_FLOAT_MANTISSA_MASK)|(1UL<<IEEE754_FLOAT_MANTISSA_BITS))) #define IEEE754_FLOAT_EXPONENT_DIGITS 38 #define IEEE754_FLOAT_EXPONENT_MAX 128 #define IEEE754_FLOAT_EXPONENT_MIN (-127) // 本当は -126 だけど。んー #define IEEE754_FLOAT_EXPONENT_RANGE (IEEE754_FLOAT_EXPONENT_MAX-IEEE754_FLOAT_EXPONENT_MIN+1) #define IEEE754_FLOAT_MANTISSA_DIGITS 7 #define IEEE754_FLOAT_POW2( exponent ) ((((exponent)-IEEE754_FLOAT_EXPONENT_MIN)<<IEEE754_FLOAT_MANTISSA_BITS)&IEEE754_FLOAT_EXPONENT_MASK)
fp64(s52e11) - IEEE 754 倍精度 double
#define IEEE754_DOUBLE_SIGN_BITS 1 #define IEEE754_DOUBLE_EXPONENT_BITS 11 #define IEEE754_DOUBLE_MANTISSA_BITS 52 #define IEEE754_DOUBLE_SIGN_MASK (0x8000000000000000ULL) #define IEEE754_DOUBLE_EXPONENT_MASK (0x7ff0000000000000ULL) #define IEEE754_DOUBLE_MANTISSA_MASK (0x000fffffffffffffULL) #define IEEE754_DOUBLE_QNAN (0x0008000000000000ULL) #define IEEE754_DOUBLE_POSITIVE_INFINITE (0x7ff0000000000000ULL) #define IEEE754_DOUBLE_NEGATIVE_INFINITE (0xfff0000000000000ULL) #define IEEE754_DOUBLE_POSITIVE_ZERO (0x0000000000000000ULL) #define IEEE754_DOUBLE_NEGATIVE_ZERO (0x8000000000000000ULL) #define IEEE754_DOUBLE_MAX (0x7fefffffffffffffULL) // < 2<sup>+1024</sup> #define IEEE754_DOUBLE_MIN (0x0010000000000000ULL) // == 2<sup>-1022</sup> #define IEEE754_DOUBLE_EPSILON (0x3cb0000000000000ULL) // == 2<sup>-52</sup> #define IEEE754_DOUBLE_ISMINUS( fl ) ((fl) & IEEE754_DOUBLE_SIGN_MASK) #define IEEE754_DOUBLE_ISZERO( fl ) ( !((fl) & ~IEEE754_DOUBLE_SIGN_MASK)) #define IEEE754_DOUBLE_FINITE( fl ) (((fl) & IEEE754_DOUBLE_EXPONENT_MASK)!=IEEE754_DOUBLE_EXPONENT_MASK) #define IEEE754_DOUBLE_NORMALIZED( fl ) ((((fl) & IEEE754_DOUBLE_EXPONENT_MASK)!=IEEE754_DOUBLE_EXPONENT_MASK)&& ((fl) & IEEE754_DOUBLE_EXPONENT_MASK)) #define IEEE754_DOUBLE_ISNAN( fl ) ((((fl) & IEEE754_DOUBLE_EXPONENT_MASK)==IEEE754_DOUBLE_EXPONENT_MASK)&& ((fl) & IEEE754_DOUBLE_MANTISSA_MASK)) #define IEEE754_DOUBLE_SIGN( fl ) (((fl) >> (IEEE754_DOUBLE_EXPONENT_BITS+IEEE754_DOUBLE_MANTISSA_BITS)) & 0x0000000000000001ULL) #define IEEE754_DOUBLE_EXPONENT( fl ) ( !IEEE754_DOUBLE_NORMALIZED((fl)) ? (IEEE754_DOUBLE_EXPONENT_MIN+1) : ((((fl) & IEEE754_DOUBLE_EXPONENT_MASK)>>IEEE754_DOUBLE_MANTISSA_BITS)+IEEE754_DOUBLE_EXPONENT_MIN)) #define IEEE754_DOUBLE_MANTISSA( fl ) ( !IEEE754_DOUBLE_NORMALIZED((fl)) ? ((fl) & IEEE754_DOUBLE_MANTISSA_MASK) : (((fl) & IEEE754_DOUBLE_MANTISSA_MASK)|(1ULL<<IEEE754_DOUBLE_MANTISSA_BITS))) #define IEEE754_DOUBLE_EXPONENT_DIGITS 308 #define IEEE754_DOUBLE_EXPONENT_MAX 1024 #define IEEE754_DOUBLE_EXPONENT_MIN (-1023) // 本当は -1022 だけど。んー #define IEEE754_DOUBLE_EXPONENT_RANGE (IEEE754_DOUBLE_EXPONENT_MAX-IEEE754_DOUBLE_EXPONENT_MIN+1) #define IEEE754_DOUBLE_MANTISSA_DIGITS 16 #define IEEE754_DOUBLE_POW2( exponent ) ((((exponent)-IEEE754_DOUBLE_EXPONENT_MIN)<<IEEE754_DOUBLE_MANTISSA_BITS)&IEEE754_DOUBLE_EXPONENT_MASK)
ここで 64ビットの整数リテラルを 1ULL のように書いていますが VC6.0 と VC7.0 では ULL の代わりに I64 というサフィックスを使うので下記のようなマクロを書いておいて
// __MSC_VER // VC8.0 : 1400 // VC7.1 : 1310 // VC7.0 : 1300 // VC6.0 : 1200 #if !defined( _ULL ) #if defined( _MSC_VER ) &&( _MSC_VER < 1310 ) #define _ULL( n ) n ## I64 #else // _MSC_VER #define _ULL( n ) n ## ULL #endif // _MSC_VER #endif // _ULL
こんなふうにしてもいいかもしれません
#define IEEE754_DOUBLE_POSITIVE_INFINITE _ULL(0x7ff0000000000000)
fp16(s10e5)
#define FP16_SIGN_BITS 1 #define FP16_EXPONENT_BITS 5 #define FP16_MANTISSA_BITS 10 #define FP16_SIGN_MASK (0x8000) #define FP16_EXPONENT_MASK (0x7c00) #define FP16_MANTISSA_MASK (0x03ff) #define FP16_QNAN (0x0200) #define FP16_POSITIVE_INFINITE (0x7c00) #define FP16_NEGATIVE_INFINITE (0xfc00) #define FP16_POSITIVE_ZERO (0x0000) #define FP16_NEGATIVE_ZERO (0x8000) #define FP16_MAX (0x7bff) // < 2<sup>+16</sup> #define FP16_MIN (0x0400) // == 2<sup>-14</sup> #define FP16_EPSILON (0x1400) // == 2<sup>-10</sup> #define FP16_ISMINUS( fl ) ((fl) & FP16_SIGN_MASK) #define FP16_ISZERO( fl ) ( !((fl) & ~FP16_SIGN_MASK)) #define FP16_FINITE( fl ) (((fl) & FP16_EXPONENT_MASK)!=FP16_EXPONENT_MASK) #define FP16_NORMALIZED( fl ) ((((fl) & FP16_EXPONENT_MASK)!=FP16_EXPONENT_MASK)&& ((fl) & FP16_EXPONENT_MASK)) #define FP16_ISNAN( fl ) ((((fl) & FP16_EXPONENT_MASK)==FP16_EXPONENT_MASK)&& ((fl) & FP16_MANTISSA_MASK)) #define FP16_SIGN( fl ) (((fl) >> (FP16_EXPONENT_BITS+FP16_MANTISSA_BITS)) & 0x00000001) #define FP16_EXPONENT( fl ) ( !FP16_NORMALIZED((fl)) ? (FP16_EXPONENT_MIN+1) : ((((fl) & FP16_EXPONENT_MASK)>>FP16_MANTISSA_BITS)+FP16_EXPONENT_MIN)) #define FP16_MANTISSA( fl ) ( !FP16_NORMALIZED((fl)) ? ((fl) & FP16_MANTISSA_MASK) : (((fl) & FP16_MANTISSA_MASK)|(1<<FP16_MANTISSA_BITS))) #define FP16_EXPONENT_DIGITS 5 #define FP16_EXPONENT_MAX 16 #define FP16_EXPONENT_MIN (-15) // 本当は -14 だけど。んー #define FP16_EXPONENT_RANGE (FP16_EXPONENT_MAX-FP16_EXPONENT_MIN+1) #define FP16_MANTISSA_DIGITS 4 #define FP16_POW2( exponent ) ((((exponent)-FP16_EXPONENT_MIN)<<FP16_MANTISSA_BITS)&FP16_EXPONENT_MASK)