6

私は SDCC 2.8.0 を使用しているため、メモリとコード サイズが非常に限られています。0 から 127 の範囲の入力値があるとします。それを 20 から 100 にスケーリングしたいとします。通常は次のようにします。

int scale(int input, int min, int max)
{
 // assuming max is always greater than min
 float range = (float)max - (float)min;
 int output = min + int((range / 127.f) * (float)input);
 return output;
}

呼び出すscale(64, 20, 100);と、20 と 100 のちょうど中間にあたる 60 が得られます。

浮動小数点数を使用せずにこれを行うにはどうすればよいですか? ビットシフトマジックはありますか?

4

2 に答える 2

3

Else を除算する前に単純に(max-min)<(INT_MAX/127)乗算できる場合は 、オーバーフローや未定義の動作を回避するために操作を分解する必要があります...(max-min)*input/127

後のケースでは、両方の乗数を 127 で割る単純な可能性があります。

A=Q1*127+R1
B=Q2*127+R2
A*B = (Q1*Q2*127 + Q1*R2 + Q2*R1) * 127 + R1*R2
(A*B)/127 = Q1*Q2*127 + Q1*R2 + Q2*R1 + (R1*R2/127)

またはCで:

unsigned int range=max-min;
unsigned int output = min
    + (range/127)*(input/127)*127
    + (range/127)*(input%127)
    + (range%127)*(input/127)
    + (range%127)*(input%127) / 127;

bit-shifting を使用したより効率的な定式化があることはほぼ確実です>>8。コンパイラーは既にそれをうまく行っているかもしれませんが、それほどうまくいかないかもしれません。

A=Q1*128+R1
B= 0*128+R2 (because B<=127)
A*B = (Q1*R2) * (127+1) + R1*R2
(A*B)/127 = Q1*R2 + (Q1*R2 + R1*R2)/127

そして C:
EDIT
Ahem では、私の意図は 128 で割ること、つまり >>7 であり、>>8 と同じように書きましたが、これは &0xFF ではなく &0x7F である必要があります
。 %128 コンパイラがこれらの操作を単純なビット操作に変換することを信頼できるため...

unsigned int range=max-min;
unsigned int high=(range / 128)*input;
unsigned int low =(range % 128)*input;
unsigned int output = min + high + (high+low)/127;

EDIT2
分布のバランスを少し良くするために、次のように切り捨てではなく、ある種の丸めを適用する場合があります。

unsigned int output = min + high + (high+low+63)/127;
于 2014-01-28T19:30:41.810 に答える