• ベストアンサー

数値計算における誤差について

小数点以下5~6桁の数値(整数部は1~5桁)を何度も積算するプログラムをSolaris7とWinXPで動作させたところ、同一データを使ったにもかかわらず誤差が生じました。非常に小さい差(0.2%程度)のものなので実用上は大きな問題はないのですが、何が原因でこうなるのでしょうか。プログラムでは数値を単精度として扱っています。

質問者が選んだベストアンサー

  • ベストアンサー
  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.6

見つかりました。 以下のリンクが参考になると思います。 Solarisではデフォルトでどうなのかまでは知らないので、 ひょっとして的外れの可能性はありますが参考になれば。 Gauche:拡張浮動小数点演算の謎 http://practical-scheme.net/wiliki/wiliki.cgi?Gauche%3A%E6%8B%A1%E5%BC%B5%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%BC%94%E7%AE%97%E3%81%AE%E8%AC%8E FreeBSD QandA 587 http://www.jp.freebsd.org/QandA/HTML/587.html PHPのround関数の謎が少し解けた - hnwの日記 http://d.hatena.ne.jp/hnw/20070603 最初のリンクから。 --- 86系の浮動小数点演算ユニットのレジスタは80bitの拡張浮動小数点数 (仮数部64bit)で、floatでもdoubleでも演算は一度80bitに直してから行われ、結果が単精度または倍精度に丸められる。ところが、ある種の最適化が行われた場合、途中結果が浮動小数点レジスタに置いたままにされるため、80bitのまま演算が進行する。 その値をスタック経由で関数に渡す時にはdoubleに丸められるので、 printfに渡して印字させるだけでは違いがわからなかったのだ。 この現象はコンパイラの最適化に依存する。この例でも、-O2をつけなければ違いは発生しないし、mult10()の呼び出しをただの乗算に置き換えても発生しない。また、FreeBSDなどではプロセスの浮動小数点演算モードを明示的に拡張浮動小数点数を使うように設定してやらねばならないようである。 ---

statisticianjr
質問者

お礼

リンクの件わざわざありがとうございます。 明日会議の席上でこの誤差について説明する予定なので、 とても助かりました。 回答はこれにて打ち切りとさせていただきます。 皆さん本当にありがとうございました。

その他の回答 (5)

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.5

Windows xp は x86で間違いないとして、Solarisの方もCPUはx86ですか? #7ってx86版あったっけ? もしそうだったとして、以下のような可能性があります。 x86の浮動小数点数演算は80bitの精度で計算できます。 double と書くと 64bit精度になりますが、CPUの内部では80bitの 精度で計算されています。 これを変数の領域に取り出したりするときに 80bit → 64bitの 変換がかかります。 このとき浮動小数点数の演算をするときに、コンパイラが最適化をして できる限り 80bit→64bitの取り出しをしないで計算するコードを 出力した場合と、いちいちこの変換をしながら計算を進めるコードを 出力した場合に、微妙に最下位の方の値が変る可能性があります。 たしかFreeBSDあたりでこの辺が原因のバグだかなんだかが あったような記憶があります。 #一応調べてみます って、単精度だから32bitか。 アセンブリ言語読める人を捕まえて、コードを解析して貰ったら良いかと。

statisticianjr
質問者

お礼

回答ありがとうございます。 知人から「CPUの最適化がどうこう」ということを聞いたのですが、知人自身も最適化がどういうことかよく分からないと言っていました。 この頂いた回答で最適化の意味が分かりました。 単精度においても「コンパイラが最適化をしてできる限り 80bit→32bitの取り出しをしないで計算する場合とそうでない場合がある」ということがあるとすれば説明が付くような気がします。

  • Donotrely
  • ベストアンサー率41% (537/1280)
回答No.4

#2です。 Solaris7と聞いててっきりSparcと思ってましたが、 質問文を見返すと必ずしもそうではないですね。 もしCPUが違えば#2の通りですが、もし同じならまず実行したコードの差が考えられます。 アルゴリズムやコンパイラ、オプチマイズなどに起因した計算内容の差異です。 演算の順番が違っただけで演算結果は違ってきます。 またもしかしたら数値の標準形式がSolarisとXPで違っているかもしれません。 結果を求めた後でその変換をするとそこで差が出ることもあり得ます。

statisticianjr
質問者

お礼

再度の回答、ご丁寧にありがとうございます。 すみません、私が質問に書き漏らしたのですがプログラムも同一のものなのでアルゴリズムの差ではないと思います。 身近な人に聞いたところ「OS、コンパイラの相違による差異」という答が多く、原因はやはりこの辺らしいです。

  • usokoku
  • ベストアンサー率29% (744/2559)
回答No.3

数値計算の入門書のはじめの頃に乗っているはずですが 計算機eの問題です。 単精度実数は、7.2桁ですから、無限小数(2新法で考えてくたざい)の取り扱いなので、実数部4桁ですと小数部2桁目付近で十進化・二進化の誤差が出てきます。その後の演算で、二進演算の誤差が出てきます。 これも、使っている計算機で変わるので、一概に言えません。 WinXpの使用経験はありませんので、異なっている場合があります。

statisticianjr
質問者

お礼

ご回答ありがとうございました。 この二進演算の誤差がSolaris7とWinXPで異なると言うことですね。

  • Donotrely
  • ベストアンサー率41% (537/1280)
回答No.2

内部データの形式と浮動小数点演算仕様の差に起因すると思います。 丸め誤差は出るかもしれませんが、データの形式と演算仕様が同じなら、 同じ丸め誤差が発生し、差は出ないはずです。 たぶん単精度ということなら両方とも指数1Byte + 仮数3Byteの計4Byteだと思います。 仮数の形式は符号+絶対値だったり、2の補数表現だったりします。 指数は2の補数表現だったり+128のバイアスがかかっていたりし、また基数が2だったり16だったりします。 さらに、演算途中でガードディジットを持ったり持たなかったりで、 精度に差が出ます。 大まかな差異原因はそんなところでしょう。 ちなみに、もしAMDがインテルの完全互換だったら、差は出てはいけないはずです。 もし差が出たら、完全互換とは言えません。

statisticianjr
質問者

お礼

ご回答ありがとうございました。 Solaris7とWinXPで違いが発生する可能性のある箇所が具体的に記述されているので、非常に助かりました。

  • nrb
  • ベストアンサー率31% (2227/7020)
回答No.1
statisticianjr
質問者

お礼

迅速な回答、ありがとうございました。 この丸め誤差がSolaris7とWinXPでは異なると言うことですね。

関連するQ&A