2017年11月11日土曜日

メモ:浮動小数点数

 浮動小数点数は誤差があるから使ってはいけないのだという。例として、「0.1ですら性格に表現できない」とか言われている。実際、JSF++でも浮動小数点数の演算は一部の例外を除き、明確に禁止されている。

 そもそもなぜ浮動小数点数は誤差を持つかというと、大抵の実数は2進数の有限長では表現できないためで、整数値の範囲であれば問題はない。例えば整数同士の加減乗は問題ない。除算の場合は、整数でも1.0/3.0=3.33...のように実数となり、また有限長の2進数では表現できない。

 じゃぁdoubleでも実数しか使わないなら問題ないね、ということになるが、それならint使ったほうが良いだろ、となってしまう。

 参考までに、浮動小数点数の表現範囲は、単精度が23bit、倍精度が52bit、4倍精度が112bitとなる(さらに符号ビットや指数がある)。
 23bitではfloor(log10(2^23))で 10進6桁分の精度がある。52bitでは15桁、112bitでは33桁となる(4倍精度はまだ一般的ではない感じだが)。
 ちなみに8bitから128bitまでだと、8bitで2桁、16bitで4桁、32bitで9桁、64bitで18桁、128bitで38桁を表現できる(符号ありで1bit少ない幅の場合)。
 浮動小数点でも整数でも、演算途中を含め、数値がこの桁数の範囲に収まるのであれば、誤差は発生しない。
 例えば1/128は0.0078125だが、これは有効数字5桁なので、単精度浮動小数点数型でも誤差は発生しない。しかし1/2048は0.00048828125で8桁が必用なので、単精度浮動小数点数型では表現できない。倍精度浮動小数点数型は15桁まで表現できるので、8桁なら問題なく表現できる。
 単精度浮動小数点数型は約6桁しか表現できないから、例えば12345678という数値も正しく表現できない。

 浮動小数点にかかわらず、プログラミングで変数を使う場合には、必要な精度を見極める必要がある。無闇矢鱈と大きな変数を使えば、丸め誤差は発生しないが、それではメモリを余計に使用してしまう。


 例えば、GPSの本によると、単精度では足りず、倍精度を使う必要があると書いてある。
 GPS衛星は約20,000kmの高度にいるから、単精度では100メートル単位でしか表現できない。これでは数百mの計算誤差を生じてしまう。倍精度ならマイクロメートルの単位まで表現することができる。
 実際にはGPSの位置情報は数メートルの誤差があるが、精密測量機器ではミリメートルの精度で計測できるし、汎用型でも誤差数百メートルは許容できないだろうから、倍精度が必用、というわけ。


 整数型で計算を行う場合、加減乗は範囲だけ気にすればいいので気が楽だが、除算はかなり面倒になる。例えば10/6は1.666を丸めた2を期待するが、整数型では切り捨てのために1となる。このあたりの誤差をどう処理するか、といった、本来の処理からは外れた部分を考える必要があるのが大変。
 STM32F4にはFPUが乗ってるんだし、多少誤差が有ったって問題ないんだから、浮動小数点を使ってもいいでしょ、とは思うのだが、一旦整数型縛りを始めるとなかなか。

0 件のコメント:

コメントを投稿