この SO の質問に対する回答は、実装に関してかなり包括的です。あちらで見たよりももう少し説明があります:
1 つのアプローチは、[-1.0,1.0 など] のように、すべての数値を範囲に強制することです。次に、それらの数値を範囲 [-2^15,(2^15)-1] にマップします。例えば、
Half = round(0.5*32768); //16384
Third = round((1.0/3.0)*32768); //10923
この2つの数を掛けると、
Temp = Half*Third; //178962432
Result = Temp/32768; //5461 = round(1.0/6.0)*32768
最後の行で 32768 で割ることは、追加のスケーリング手順が必要な乗算についてPatrosが指摘した点です。2^N スケーリングを明示的に記述すると、これはより理にかなっています。
x1 = x1Float*(2^15);
x2 = x2Float*(2^15);
Temp = x1Float*x2Float*(2^15)*(2^15);
Result = Temp/(2^15); //get back to 2^N scaling
それが算数です。実装では、2 つの 16 ビット整数の乗算には 32 ビットの結果が必要であるため、Temp は 32 ビットである必要があることに注意してください。また、32768 は 16 ビット変数では表現できないため、コンパイラは 32 ビットの即値を作成することに注意してください。そして、すでに指摘したように、2 の累乗で乗算/除算に移行できるので、次のように書くことができます。
N = 15;
SInt16 x1 = round(x1Float * (1 << N));
SInt16 x2 = round(x2Float * (1 << N));
SInt32 Temp = x1*x2;
Result = (SInt16)(Temp >> N);
FloatResult = ((double)Result)/(1 << N);
しかし、[-1,1) が適切な範囲ではないとしますか? 数値を [-4.0,4.0) などに制限したい場合は、N = 13 を使用できます。この場合、2 進小数点の前に 2 ビット、後ろに 13 ビットの 1 符号ビットがあります。これらは、それぞれ 1.15 および 3.13 固定小数点小数型と呼ばれます。ヘッドルームの分数で精度を交換します。
分数型の加算と減算は、飽和に気を付けている限り問題なく機能します。除算については、パトロスが言ったように、スケーリングは実際には相殺されます。だからあなたはしなければならない
Quotient = (x1/x2) << N;
または、精度を維持するために
Quotient = (SInt16)(((SInt32)x1 << N)/x2); //x1 << N needs wide storage
整数による乗算と除算は正常に機能します。たとえば、6 で割るには、次のように書くだけです。
Quotient = x1/6; //equivalent to x1Float*(2^15)/6, stays scaled
そして、2の累乗で割った場合、
Quotient = x1 >> 3; //divides by 8, can't do x1 << -3 as Patros pointed out
ただし、整数の足し算と引き算は単純には機能しません。まず、整数が xy 型に適合するかどうかを確認し、同等の小数型を作成して続行する必要があります。
これがアイデアに役立つことを願っています。クリーンな実装については、他の質問のコードを見てください。