2013年2月10日日曜日

浮動小数点数の精度

最近基本情報技術者を取得しようと勉強しているとあるでしへ。

先のエントリーで定数πをC++で使う簡単な方法を2つ紹介した。その例ではfloat(=IEEE754 Binary32 形式)型で実行時にπを扱える様に保持していた。

constexpr float pif = std::atan(1) * 4;

この場合、当然実行時に扱える定数pifはfloatの精度に縛られる。実際に出力を確認して見よう
3.14159
さて、これだけではこのpifで扱えるπの本当の精度は分からない。尤も3.14だとか凡そ3で十分な計算に使って構わない事は分かるが。次のように標準ライブラリーiomanipを追加して表示する桁数を増やしてみよう。

#include <iomanip>
int main() { std::cout << std::setprecision(32) << pif; }

高精度なπの値はsuper πの計算済みの結果など参考にすれば今のところ間違い無いだろう。

さて、何桁目まで合っていたかな?恐らく今回は最初の数字から7桁目までは合っていたと思う。後は数字毎に見れば出鱈目だろう。

ちなみに値の何桁目まで信用できるか有効数字と言って、計算機に限らずメスシリンダーであったり秤であったりでも有効数字何桁と言う表現を使う。

続いて、double(=IEEE754 Binary64 形式)型ではどうか確認して見よう。

namespace {
  template<class T>
  constexpr T pi() { return std::atan(1) * 4; }
}
int main() {
  std::cout
    << std::setprecision(32)
    << pi<float>() << "\n"
    << pi<double>() << "\n"
    ;
}

もののついで、C++のテンプレート関数も怖がらず積極的に使いこなせる様になる事(╹◡╹)

同じ計算式だがtemplateに指定された型パラメーターTがfloatかdoubleかに応じてreturn時に暗黙的に型変換(これは余り良く無い事だが)され、変換後の型の精度で表せる内容まで精度が落ちている。この様に浮動小数点数に置いて記録方式に依存して丸め込まれる事で生じる誤差を浮動小数点数の丸め誤差と言う。

念の為に言って置くが、ここまできちんとソースコード理解しながら書いて翻訳して結果を自分で確認する事。その為もあってここまで実行結果については掲載していない(╹◡╹)

なお、考えてみれば当然の事だが、floatで保持しているをdoubleに型変換しても値の精度は上がらない。少なくとも通常の変数は値を保持しているのであって、計算式を保持している訳じゃないからね。

さて、この浮動小数点数の取り扱いについて基礎的な注意事項を理解できたなら、京都産業大学の山田修二教授がまとめた簡潔な資料「浮動小数点数と誤差」が部外者でもオンラインで見られる様なのでありがたく読んで見よう(元はたぶん教授の授業用のページだろう)。理解できればもう浮動小数点数と誤差についての知識については心配要らない

そうそう、現実問題としてネイティブコードアプリケーションが浮動小数点数を計算する場合、x86系のCPUを用てFPUを使用するアセンブリが実行されたなら、その計算内部の精度は80bitだったりする。FPUではなくSSEを使う方法ではIEEE754の通りだ。余力があればその辺り、浮動小数点数の計算とハードウェア事情についても調べてみよう(とっかかりはherumi氏の「float型とdouble型」とか、詳細は技術資料とか)。きっと、その知識もまた君の力になるだろう(╹◡╹)

0 件のコメント:

コメントを投稿