固定小数は、整数型を構成するビット列のうち、整数部と小数部にわけます。
ビット番号 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
役割 | 整数部 | 小数部 |
固定小数のビットは、2のマイナス乗、つまり 1/2, 1/4, 1/8〜 に対応しています。
ビット番号 | 〜 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
値 | 8 | 4 | 2 | 1 | 1/2 | 1/4 | 1/8 | 1/16 | 1/32 | 1/64 | 1/128 | 1/256 | |
役割 | 整数部 | 小数部 |
例(0000.0000(2) を2進数の固定小数、小数点の位置が '.' とする)
C 言語では、固定小数型が無い(プログラム言語が対応していない) ので、プログラマが整数部と小数部の管理をしなければなりません。
int i = 5; /* 整数5 */ int d; /* d をシフト数8の固定小数型とする(プログラムとしては整数型) */ d = i << 8; /* 固定小数型変数 d に整数型の値 i = 5を代入する */ printf( "i = %d", i ); /* "i = 5" と表示される */ printf( "d * 256 = %d", d ); /* "d * 256 = 1280" と表示される */ printf( "d = %d", d >> 8 ); /* "d = 5" と表示される(このように戻さなければならない) */ |
固定小数 d のシフト数が SFT の場合、 固定小数としている整数型には、2の SFT 乗をかけた値が入っています。
d = i << SFT; = i * 2^SFT; /* 2^SFT は、2 の SFT 乗のこと */ |
小数を代入するときは、2のシフト数乗をかけた値を指定します。
int d; /* シフト数8の固定小数 */ /* d = 1.23 */ d = 315; /* 1.23 * (2の8乗) = 1.23 * 256 = 314.88 */ |
[ 変換 | 四捨五入 | 代入 | 加減算 | 乗算 | 除算 ]
整数と固定小数を変換するときは、 次のようにシフト演算を用います。 四捨五入するときは、符号に注意。
#define SFT 8 #define HALF 1 << (SFT - 1); /* HALF = 0.5 */ int i; /* 整数 */ int d; /* 固定小数、シフト数 SFT */ d = i << SFT; i = d >> SFT; /* 四捨五入するとき */ if ( d >= 0 ) i = ( d + HALF ) >> SFT; else i = ( d - HALF ) >> SFT; |
固定小数への代入は固定小数で行います。 固定小数の数値を代入するときは 2のシフト数乗をかけた値を指定します。
#define SFT 8 int i; /* 整数 */ int d1, d2, d3; /* 固定小数、シフト数 SFT */ d1 = d2; d3 = i << SFT; d2 = 315; /* d = 1.23 */ /* ∵ 1.23 * (2の8乗) = 1.23 * 256 = 314.88 */ |
固定小数の加減算は固定小数に桁を合わせて行います。
#define SFT 8 int i; /* 整数 */ int d1, d2, d3; /* 固定小数、シフト数 SFT */ d2 = d1 + ( i << SFT ); d2 = d1 + ( 5 << SFT ); /* d1 + 5 */ d3 += d2; |
乗算は、int 型と乗算するか、計算後にシフト数だけ戻します。
オーバーフローによる計算不可や切捨てによる誤差に注意。
シフト数だけ戻すときに四捨五入するときは、符号に注意。
#define SFT 8 #define HALF 1 << (SFT - 1); /* HALF = 0.5 */ int i; /* 整数 */ int d, d1, d2; /* 固定小数、シフト数 SFT */ int f; /* 整数型と乗算するときは、そのまま */ d *= i; /* 小数の精度が求められるときは、計算後にシフト数だけ戻す。ただし、オーバーフローに注意 */ d = d1 * d2; if ( d >= 0 ) d = (d + HALF) >> SFT; else d = (d - HALF) >> SFT; /* オーバーフローが気になるときは、計算前に整数型に戻す。 */ /* 左辺を切り捨てるとき */ d1 = ( d1 >= 0 ) ? (d1 + HALF) >> SFT : (d1 - HALF) >> SFT; d = d1 * d2; /* 右辺を切り捨てるとき */ d2 = ( d2 >= 0 ) ? (d2 + HALF) >> SFT : (d2 - HALF) >> SFT; d = d1 * d2; |
除算も、int 型と行うか、計算後にシフト数だけ戻します。
ただし計算結果が整数型のときは、固定小数同士を除算します。
オーバーフローによる計算不可や切捨てによる誤差に注意。
シフト数だけ戻すときに四捨五入するときは、符号に注意。
#define SFT 8 #define HALF 1 << (SFT - 1); /* HALF = 0.5 */ int i, i1, i2; /* 整数 */ int d, d1, d2; /* 固定小数、シフト数 SFT */ int f; /* 整数型で除算するときは、そのまま */ d = d1 / i; /* 小数の精度が求められるときは、計算前に左辺をシフトする。ただし、オーバーフローに注意 */ d = ( d1 << SFT ) / d2; /* 左辺のオーバーフローが気になり、計算後の小数の精度が求められるときは、右辺を切り捨ててから計算する */ i2 = ( d2 >= 0 ) ? (d2 + HALF) >> SFT : (d2 - HALF) >> SFT; d = d1 / i2; /* 左辺のオーバーフローが気になり、右辺に小数の精度が求められるときは、計算後にシフトする */ d1 = ( d1 >= 0 ) ? d1 + HALF : d1 - HALF; d = ( d1 / d2 ) << SFT; /* 計算結果が整数型のときは、四捨五入してから、固定小数同士をそのまま除算する */ d1 = ( d1 >= 0 ) ? d1 + HALF : d1 - HALF; i = d1 / d2; |