gccには-Ofast
というオプションが存在する。これは、標準規格への適合性を犠牲にしてでも高速なバイナリを吐くモードだ。色々なことが犠牲になるが、NaNチェックも犠牲になる。
以下のコードを見て欲しい。
#include <iostream> #include <cassert> #include <cmath> int main() { const double x = -std::log(-1.0); std::cout << "x = " << x << std::endl; std::cout << std::boolalpha; std::cout << "x is nan? " << std::isnan(x) << std::endl; std::cout << "x is finite? " << std::isfinite(x) << std::endl; assert(std::isfinite(x)); assert(not std::isnan(x)); return 0; }
Start x = nan x is nan? false x is finite? true 0 Finish
嘘をつくな。
ちなみに以下の通り、-O3
ならチェックはする。
気になるのでちょっとアセンブリも見てみよう。
check(): mov eax, 1 ret
即値1を返していて草。
ちなみに以下の通り、引数によらずisfinite
が全てtrue
に変換されている。
テストコードのコンパイルの際は気をつけましょう。
糸冬 --------------- 制作・著作 GCC
追記:
流石にちょっと雑かなと思ったので。-Ofast
は-ffast-math
というフラグを自動で立てるのだがこれはIEEE754に厳密には従わない(が概ね問題がなく高速な)コードを生成する。例えば浮動小数点数は足す順序を変えただけで少し結果が変わってしまったりするので、数式だと思えば意味が同じコードでもちょっと結果が変わったりするのだが、-ffast-math
を立てるとそういう最適化をしたりしなかったりする。
で、-ffast-math
はいくつかのフラグを一気に立てるためのメタなフラグで、このなかに-ffinite-math-only
というフラグがある。これは、inf
やNaN
が出てこないと仮定して(ちょうどコンパイラがUBを利用するように)最適化を行うフラグで、当然諸々のチェックは取り除かれる。Inf
もNaN
も出てこないと思って最適化していいので、InfかNaNだったときの分岐を消すのは当然だ。
もう少し細かく話すと以上のような感じになっている。