残差計算のオーバーフロー
コンピュータの世界では長さや強さなどの物理量をデジタル(0と1)で扱うため量子化誤差というものが生じることがあります。
たとえば整数演算で 10 / 3 を計算すると結果は 3 となります。整数演算で切り捨てられた 1/3 (≒0.33333) が整数演算における量子化誤差です。
組込みシステムにおいては、機械的な制約から量子化誤差が発生することがあります。たとえばあるユニットを 33.3ミリメートル移動させたいのだけれど機械的な制約で1ミリメール単位でしか移動させることができない。仕方がないから 33 ミリだけ動かそう、とやります。しかし 100ミリメートルの移動を3回に分割して移動させようとすると次のような不都合が生じます。
あれ? 100ミリメートルを3回に分割して移動させたはずなのに 99ミリメートルしか移動していない。困ったな、となります。切り捨てがマズいなら四捨五入にしよう!と変更しても、3回分の移動結果は同じです。
上記の説明では量子化の幅(離散化の間隔)を 1ミリメートル としましたが 実際の機器では 0.3ミリメートル かもしれませんし 5ミリメートル かもしれません。また量子化の幅が等間隔である線形量子化はシンプルですが、0~10ミリメートルは量子化幅 1ミリメートル, 10~100ミリメートルは量子化幅10ミリメートルといった具合に量子化幅が変化する非線形量子化というものを採用することもあります。
いずれにしても、現実世界の物理量をデジタル(離散値)に置き換えるときに、量子化誤差を避けることは困難です。
この量子化誤差をゼロにすることを諦めたとしても、誤差の影響を軽減させることができる方法のひとつが量子化残差(累積量子化誤差)の計算になります。
残差計算の例は次のようになります。
『理想の制御量』と『量子化で丸めた実際の制御量』の差(量子化誤差)を計算します。たとえば量子化幅を 5 と仮定し、理想制御量を 103 とすると、量子化で丸めた制御量は 100、量子化誤差が 3 となります。この量子化誤差を残差に積みます。
つぎの理想制御量が 206 だとすると量子化で丸めた制御量は 205、量子化誤差は +1 となります。この +1 を残差に積み増すと +4 が更新後の残差となります。
つぎの理想制御量が 111 だとすると量子化で丸めた制御量は 110、量子化誤差は +1。残差に積み増すと +5 が更新後の残差となります。
残差が量子化幅に達したため、残差に残っていた 5 を加えて、次の実際の制御量を 115 とします。ここまでに丸めてきた量子化誤差を、このタイミングで取り戻すことになります。
量子化誤差を福引の補助券に例えるとわかりやすいかもしれません。5ポイントに達するまでは福引を回せない。しかし補助券を貯めておいて、5ポイント貯まったところで福引券1回に交換する。といった感じです。
演算結果のオーバーフローが発生したのは次の関数です。
unsigned char quantize_x(unsigned char x) {
const unsigned char STEP = 10;
static unsigned char carryover = 0;
unsigned char result = (x + carryover) / STEP * STEP;
carryover += x - result;
return (result);
}
量子化では切り捨てが行われるため入力(関数引数)よりも出力(関数返却値)が大きくなることは想定していませんでした。しかしながら量子化残差が加わることで入力よりも出力が大きくなるときがあります。そして、そのときにたまたまオーバーフローが発生して制御結果がおかしくなっていました。
この記事が気に入ったらサポートをしてみませんか?