●固定小数

固定小数は、整数型を用いて小数を表現したものです。
浮動小数型よりも速く計算することができます。
整数型から固定小数型に変換するにはシフト演算("<<", ">>")を使います。
求められる小数の精度に応じて、シフトするビット数(シフト数)を調節します。


1. 固定小数の表現の仕方

固定小数は、整数型を構成するビット列のうち、整数部と小数部にわけます。
ビット番号 31302928 27262524 23222120 19181716 15141312 111098 7654 3210
役割整数部小数部
(32ビットの整数型、シフト数が8の場合)

固定小数のビットは、2のマイナス乗、つまり 1/2, 1/4, 1/8〜 に対応しています。
ビット番号 111098 7654 3210
 8421 1/21/41/81/16 1/321/641/1281/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 */


2. 固定小数の計算の仕方

[ 変換 | 四捨五入 | 代入 | 加減算 | 乗算 | 除算 ]

整数と固定小数を変換するときは、 次のようにシフト演算を用います。 四捨五入するときは、符号に注意。
#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;