プログラムTIPS

DEMO PROGRAM ヘキサドライブでは様々な研究開発をしています。その一部を紹介。

C++:浮動小数点誤差(2009年11月26日)

今日は浮動小数点誤差についてお話します。
プログラムを始めた頃は誰もがハマってしまう道ではないかと思います。

 
C言語にはfloatという小数を扱える型(浮動小数点型)があります。
ところが、意外なことに正確な 0.1 を表現できないのです。
これだけ技術が進歩した世の中なのに、なぜなのでしょうか…。

 
コンピュータは2進数で数値を記憶します。
floatの場合、一般的には
・符号部 1 ビット
・指数部 8 ビット
・仮数部 23 ビット
といった形になります。(特殊なハードは除きます)

 
0.1を2進数で表現する場合、
001111011100110011001100110011001100110011001100…
と割り切れない値(循環小数)になってしまうため、どこかで区切らないといけません。
32bit float の場合、33bit目が丸められて
00111101110011001100110011001101
という形になります。

 
このように最後のビットが丸められるため、正確な値を保持できません。

例えば次のような処理を行ったとします。

[c]float val = 0.0f;<br />
for( int i=0; i<10; i++ ) {<br />
val += 0.1f;<br />
}</p>
<p> if( val != 1.0f ) {<br />
printf(&quot;val は 1.0です&quot;);<br />
} else {<br />
printf(&quot;val は 1.0ではありません&quot;);<br />
}[/c]

10回0.1を足しているので1になってくれないと困るのですが、「1.0ではありません」と表示されます。
これは上記の丸め誤差があるため、正確に1.0にならないためです。
浮動小数点を=で比較するのはいけません。
ではどう比較すれば良いのでしょうか?

 
続きはWEBで…

 
というのはさておき、
「一定の範囲で比較する」というのが答えです。

 


でもさすがに0.1の誤差は大きすぎます。

 
そんな時に使用するのが FLT_EPSILON という定義です。
FLT_EPSILON は float.h で定義されているもので、
値としては 1.192092896e-07F (つまり0.0000001192092896) となっています。
定義内容としては「1.0+FLT_EPSILON !=1.0 となる最小値」となっています。
ちょっと乱暴ですが、分かりやすく言うと「すごく小さな値」ということになります。

 
浮動小数点の誤差を考慮する場合、この値を利用して判定するのが一般的です。

[html]&amp;amp;lt;br /&amp;amp;gt;if( ((1.0f – FLT_EPSILON) &amp;amp;lt; val) &amp;amp;amp;&amp;amp;amp; (val &amp;amp;lt; (1.0f + FLT_EPSILON)) ) {&amp;amp;lt;br /&amp;amp;gt; printf("val は 1.0です");&amp;amp;lt;br /&amp;amp;gt; } else {&amp;amp;lt;br /&amp;amp;gt; printf("val は 1.0ではありません");&amp;amp;lt;br /&amp;amp;gt; }&amp;amp;lt;br /&amp;amp;gt;[/html]

使用用途などによってはFLT_EPSILONでは判定できない、なんてこともあるかと思います。
そういうときは
・単純に判定する範囲を広げる
・double型を使用して精度を上げる(double型の場合はDBL_EPSILONになります)
・固定小数点(整数型で小数点を表現する)
など、いろいろ方法はあると思います。

浮動小数点にはこういった誤差が存在しますので、単純な比較をしないよう、十分ご注意下さい!

【免責事項】

本サイトが提供している情報に関しては、安全性等、いかなる保証もされません。 株式会社ヘキサドライブは、これらの情報をあなたが利用することによって生ずるいかなる損害に対しても一切責任を負いません。

【著作権】

本サイトが提供しているコンテンツについては、特に断りのある場合を除き、株式会社ヘキサドライブが著作権を有します。