プロが教えるわが家の防犯対策術!

ouble の計算結果が状況によっておかしくなる場合があるのですが、
理由がなかなか分からないので心当たりがあれば教えてもらえませんか。

問題のコードを簡略化したのが以下です。
------------------------------------
void calc(double *result) {
  const double mPI2 = 1.5707963267948966;
  double b_arc;
  b_arc の計算 … (1)
  // printf("test"); … (2)
  *result = mPI2 - b_arc; … (3)
}
------------------------------------

おかしくなるのは(3)の *result の値です。

(3)での b_arc、mPI2 の値を出力してみると
b_arc: [1.57079632679489660000000000000000000000]
mPI2: [1.57079632679489660000000000000000000000]
となり、mPI2 == b_arc の結果も true になります。
さらに、バイナリで見ても二つは全く同じ値でした。

なので *result は 0 になるはずなのですが、これが 0 にならない場合があります。
色々な状態で試したところ結果は以下のようになりました。

1. Windows7 : 0
2. WindowsXP : -0.000000000000000001951563910473908
3. WindowsXPのデバッグビルド : 0
4. WindowsXPで(2)(2)のコメントアウトを外し printf をいれる : 0

WindowsXPのリリースビルドだけ 0 にならず、 -0.000000000000000001951563910473908 になります。
4は理由が分からないのですが、(2)の位置になんらかの処理をいれると結果が 0 になります。

ちなみにVC++ではなくQTというFrameworkを使って作っています。

よろしくお願いします。

A 回答 (3件)

おそらく、下記リンクにあるような浮動小数用レジスタの扱いが原因だと思います。


http://0xcc.net/blog/archives/000164.html

doubleはメモリ上では64bitですが、実際にプロセッサ内で計算する場合には、モードや動作環境にもよりますが64bit以上のサイズのレジスタ(80bit)が使われることが多いようです。

したがって、レジスタにはいっている値をメモリに戻す時に精度の低下が発生します。

b_arc: [1.57079632679489660000000000000000000000]

とありますが、プロセッサ内で浮動小数演算を連続して実行し続けている場合、レジスタ内ではもう少し値が入っており、

b_arc: [1.5707963267948966********]

という感じになっているはずです。そこから

mPI2: [1.57079632679489660000000000000000000000]

を引くため、正しい計算結果としては 0.0000000000000000******** となり、この差分が

> 2. WindowsXP : -0.000000000000000001951563910473908

では無いかと思います。

> 4は理由が分からないのですが、(2)の位置になんらかの処理をいれると結果が 0 になります。

というのは、途中で別の命令が入ってくるため浮動小数演算がいったん中断され、80bitレジスタの値を64bitのメモリにダウンコンバートしながら移動する、すなわちb_arcの値を

[1.5707963267948966*****] → [1.57079632679489660000000000000000000000]

と変換する処理が途中に入ってしまい、結果としてmPi2==b_arcとなっている、というのが理由だと思います。
    • good
    • 0
この回答へのお礼

どうもありがとうございます。
リンクも参考になりました。

No1の方も同じことをおっしゃっていますし、
その可能性が高いということで対応していこうと思います。

お礼日時:2012/01/24 12:47

余談ですが「VC++ではなくQTというFrameworkを使って作っています」といわれてもねぇ.


コンパイラを確定させてほしい....

この回答への補足

コンパイラはGCC(MinGW)です。

補足日時:2012/01/24 12:45
    • good
    • 0

憶測ですが……。



b_arc の計算で、三角関数の計算があるとして、
b_arc に結果が代入される直前の、拡張精度(double より、さらに桁数の多い計算、計算の中間で使われることはある)の変数を、最適化の結果として、*result への代入に使っているのではないかなという気はします。

だから、result への代入の前に別の処理が入ると、この最適化ができずに、b_arc の値をまじめに使って計算しているとか。

アセンブラに落ちた結果を確認すると良いですが。
もしくは、リリースビルドの最適化をOFFにして結果がどう変わるか確認してみるとか。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
確かにそれだと何か処理をいれると値が変わることに説明がつきますね。

ネットで調べてみたのですが、アセンブラは値が見れませんでした。
全然アセンブラの知識がないので調べるのはちょっと時間がかかりそうです。

お礼日時:2012/01/23 17:17

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!