2015/12/16(水)cygwinのsqrtの丸めモード変更
問題は、cygwinのsqrtで丸めの向きが最適化をかけないときに変わらないというものです。環境は64bitのwindows7で、cygwinも64bitの最新のものです。gccは4.9.3でした。
#include <stdio.h>
#include <math.h>
#include <fenv.h>
int main()
{
volatile double x, y, z;
x = 2.;
fesetround(FE_DOWNWARD);
y = sqrt(x);
fesetround(FE_UPWARD);
z = sqrt(x);
fesetround(FE_TONEAREST);
if (y == z) {
printf("sqrt error\n");
}
}
このようなプログラムで、cc cygwin-sqrt.cのように最適化オプションをつけないでコンパイルしたとき、丸めの向きが上向きでも下向きでも結果が同じになってしまいます。
cc -O3 cygwin-sqrt.cのように最適化をかければ問題ありません。いろいろ試すと、-O0ではアウト、-O1, -O2, -O3はセーフでした。
volatileを使わない場合最適化をかけることで結果がおかしくなるのはさほど珍しくありませんが、このように最適化をかけない状態で結果が異常になるのはとても珍しいです。
cc -Sでアセンブリソースを表示させてみると、最適化無しでは単に外部のsqrt関数を
call sqrt
と呼んでいるのに対して、-O3だと
sqrtsd %xmm0, %xmm1
のようにSSE2で直接計算するコードが生成されているので、どうやら外部(libm?)のsqrtに問題がありそうです。以前にVisual C++で似たようなことがあったので、それとも関係あるのでしょうか。どなたか詳しい方に教えて欲しいものです。
これで丸めの向きが変わらないのはIEEE754的にまずいので、そんな腐った環境は無視するというのも一つの考え方でしょうが、こういう事例が多くあるようならsqrtに関しては丸めモード変更に期待しないで何とかすることを検討するべきかも。kvライブラリでいえば-DKV_NOHWROUNDを付ければ問題なくなるので、それをデフォルトにするとか。