-OfastはNaNチェックを無効化する

gccには-Ofastというオプションが存在する。これは、標準規格への適合性を犠牲にしてでも高速なバイナリを吐くモードだ。色々なことが犠牲になるが、NaNチェックも犠牲になる。

以下のコードを見て欲しい。

wandbox.org

#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ならチェックはする。

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

気になるのでちょっとアセンブリも見てみよう。

godbolt.org

check():
        mov     eax, 1
        ret

即値1を返していて草。

ちなみに以下の通り、引数によらずisfiniteが全てtrueに変換されている。

https://godbolt.org/z/22TJMu

テストコードのコンパイルの際は気をつけましょう。

      糸冬
---------------
 制作・著作 GCC

追記:

流石にちょっと雑かなと思ったので。-Ofast-ffast-mathというフラグを自動で立てるのだがこれはIEEE754に厳密には従わない(が概ね問題がなく高速な)コードを生成する。例えば浮動小数点数は足す順序を変えただけで少し結果が変わってしまったりするので、数式だと思えば意味が同じコードでもちょっと結果が変わったりするのだが、-ffast-mathを立てるとそういう最適化をしたりしなかったりする。

で、-ffast-mathはいくつかのフラグを一気に立てるためのメタなフラグで、このなかに-ffinite-math-onlyというフラグがある。これは、infNaNが出てこないと仮定して(ちょうどコンパイラがUBを利用するように)最適化を行うフラグで、当然諸々のチェックは取り除かれる。InfNaNも出てこないと思って最適化していいので、InfかNaNだったときの分岐を消すのは当然だ。

もう少し細かく話すと以上のような感じになっている。