固定少数の計算


固定少数とは?

固定小数は、整数型を用いて小数を表現したものです。浮動小数型よりも速く計算することができます。
そのためAfterEffectsでは実数計算は実際には整数計算で行っています。

AfterEfefctsではPF_Fixedとしてa.hで定義されていて実態はlong(32Bit)です。
上位16Bitが整数部、下位16Bitが小数部として扱います。

つまりPF_Fixedで数値1を表すには実際には(1L<<16)つまり65536と表記しなければいけません。

PF_Fixedは、AfterEffectsでは座標やパラメータに良く使われています。
余談ですが32Bitパラメータ等で扱う数値は、-32768<数値<32768 となります。実際にエフェクトコントロールでは は-32000から32000までの数値しか入力できません。

型変換

PF_Fixedの変数を普通の整数にするには単純にシフトすればよい。
AE_Macros.hにそのためのマクロが用意されている。

以下はその部分を抜粋。(これ以外にも使えるマクロが定義されているので目を通しておいたほうがいい)
#define FIX2INT(X)        ((long)(X) >> 16)
#define INT2FIX(X)        ((long)(X) << 16)
#define FIX2INT_ROUND(X)  (FIX2INT((X) + 32768))
#define FIX_2_FLOAT(X)    ((double)(X) / 65536.0)
#define FLOAT_2_FIX(F)    ((PF_Fixed)((F) * 65536 + (((F) < 0) ? -0.5 : 0.5)))
あ、小数部だけ抜き出したいときは0xFFFFとand演算すればいい。
#define DECIMAL(X)  (X & 0xFFFF)

加算・減算

PF_Fixed同士の加算・減算は特に注意することはない。単純にすればいい。
たたし通常の整数値と行う場合は、ちゃんと変換してから行わないといけない。
結局 longとPF_Fixedは同じものなのでコンパイラはエラーを出さないのでバグの温床になる。
PF_Fixed A = 1L<<16;
long B = 1;
long C;
PF_Fixed D;
C = A + B;
//Cの結果は 整数で65537 固定少数では1.0000152587890625となる

C = FIX2INT(A) + B;
D = A + INT2FIX(B);
//
このような感じにやらないと予想外の結果になる(僕は良く間違える^^;)

乗算・除算

乗算・除算はかなりめんどくさい。小数部の分だけ計算結果が繰り上がってしまうので 計算後、桁の補正を行う必要がある。
(例) c = a * b >> 16; c = a / b << 16;

乗算の例
PF_Fixed A = 16L<<16;
PF_Fixed B = 4L<<16;
PF_Fixed C;

C = A * B;
//上記では 16*65536 * 4*65536 となるので計算結果は274877906944 シフトしても4194304

C = A * B >> 16;
//これで正解の(64L<<16)になる

除算の例
PF_Fixed A = 16L<<16;
PF_Fixed B = 4L<<16;
PF_Fixed C;

C = A / B;
//上記では (16*65536) / (4*65536) となるので計算結果は4 つまり0.00006103515625になってしまう

C = A * B << 16;
//これで正解の(4L<<16) 262144になる


オーバーフロー

数値が実際には2Byteしか使えないので、計算結果が32768より大きい、或いは-32768より小さくなる場合 オーバーフローをおこしとんでもない数値に化ける可能性がある。
実際の数値ではそんな大きな数値を扱う場面は少ないが、PF_Fixed同士の乗算の場合には必ず考慮しておく事が重要になる。

一番簡単な対処は、小数部の精度を犠牲にする方法で、例を以下に示す。
実際には、取りうる数値の範囲を考慮して工夫する必要がある。
PF_Fixed kakeruFix(PF_Fixed s,d)
{
	long ss = s;
	long dd = d >> 4; //小数部を4Bitなくす
	
	//乗算する。4bit分を考慮して補正する
	return (PF_Fixed)( ss * dd >> (16-4) );

}

-index-