doubleの精度が-O0だと80ビットで-O3だと64ビットになるケース

誰か同じ罠に嵌る and/or 気づくかと思い「秘密のエントリ」「あとで公開する」としていましたが、だれもハマってくれないようなので公開します。

例のPHPのround問題の関係でgccの-ffloat-storeを試していたのですが、

static double f(double x) {
  return x * 100.0;
}
int main() {
  return f(1.075) >= 107.5;
}

というプログラムをコンパイルして動かすと下のようになります。

> uname -mrs
Linux 2.6.9-42.0.10.ELsmp i686
> gcc --version
gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-8)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

> gcc -O0 hoge.c -o hoge && ./hoge ; echo $status
0
> gcc -O3 hoge.c -o hoge && ./hoge ; echo $status
1
> gcc -O0 -ffloat-store hoge.c -o hoge && ./hoge ; echo $status
0
> gcc -O3 -ffloat-store hoge.c -o hoge && ./hoge ; echo $status
1

アセンブリコードを見るとわかるのですが、どうやら-O0のほうではfの返り値がメモリにストアされず、FPUのレジスタ(というかx86なのでスタックマシンですが)に乗ったままで比べられるようです。一方で-O3のほうはfがインライン展開されるので、-ffloat-storeをつければ乗算の結果はメモリにストアされます。また、-O3をつけて-ffloat-storeをつけないと、コンパイル時の定数畳み込みにより、やはり64ビットで結果が計算されます。

要するに「-ffloat-storeか-O0をつければdoubleの精度がすべて64ビットになる」とは限らず、それどころか「-O3を-O0にしたら精度が向上する」こともあるようです。これはgccのバグなのか仕様なのか…? もっと新しいバージョンのgccでどうなるかはまだ試していませんgcc version 4.7.2 (Debian 4.7.2-5)の-m32でも-ffloat-storeをつけないほうは同様の結果になることを確認しました(-O0 -ffloat-storeのほうは1になります)