10

サンプルコード:

#include <iostream>
#include <cmath>
#include <stdint.h>

using namespace std;

static bool my_isnan(double val) {
    union { double f; uint64_t x; } u = { val };
    return (u.x << 1) > (0x7ff0000000000000u << 1);
}

int main() {
    cout << std::isinf(std::log(0.0)) << endl;
    cout << std::isnan(std::sqrt(-1.0)) << endl;
    cout << my_isnan(std::sqrt(-1.0)) << endl;
    cout << __isnan(std::sqrt(-1.0)) << endl;

    return 0;
}

オンライン コンパイラ

を使用-ffast-mathすると、そのコードは "0, 0, 1, 1" を出力します。使用しない場合は、"1, 1, 1, 1" を出力します。

あれは正しいですか?std::isinf/はこれらの場合std::isnanでも機能するはずだと思いました。-ffast-math

また、どうすれば無限大/NaNをチェックでき-ffast-mathますか? これを行っていることがわかり、my_isnan実際に機能しますが、そのソリューションはもちろんアーキテクチャに大きく依存しています。また、なぜmy_isnanここで機能し、std::isnan機能しないのですか? __isnanとはどうですか__isinf。彼らはいつも働いていますか?

では、と-ffast-mathの結果は何ですか。未定義になるか、NaN / -Inf にするか。std::sqrt(-1.0)std::log(0.0)

関連する議論: (GCC) [バグ libstdc++/50724] 新規: g++ の -ffinite-math-only によって isnan が壊れる(Mozilla) バグ 416287 - isNaN によるパフォーマンス改善の機会

4

1 に答える 1

18

-ffast-mathコンパイラが IEEE 仕様を無視/違反する可能性があることに注意してください。 http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Optimize-Options.html#Optimize-Optionsを参照してください。

このオプションは、-Ofast 以外の -O オプションではオンになりません。これは、数学関数の IEEE または ISO 規則/仕様の正確な実装に依存するプログラムの出力が正しくない可能性があるためです。ただし、これらの仕様の保証を必要としないプログラムでは、より高速なコードが生成される場合があります。

したがって、-ffast-mathyou を使用しても、本来あるべき場所に無限が見えるとは限りません。

特に、-ffast-mathをオン-ffinite-math-onlyにします。 http://gcc.gnu.org/wiki/FloatingPointMathを参照してください。最適化オプション)

[...] 引数と結果が NaN または +-Infs ではないことを前提とする浮動小数点演算の最適化

これは、 を有効にすること-ffast-mathで、コードが無限大または NaN を使用しないことをコンパイラーに約束することを意味します。これにより、コンパイラーは、たとえば、定数への呼び出しisinfまたはisnan定数による呼び出しを置き換えることによってコードを最適化できますfalse(さらに、そこの)。コンパイラに対する約束を破ると、コンパイラは正しいプログラムを作成する必要がなくなります。

したがって、答えは非常に単純です。コードに無限大または NaN が含まれている可能性がある場合 (これは、isinfandを使用するという事実によって強く暗示されます)、無効なコードを取得する可能性があるため、isnan有効にすることはできません。-ffast-math

my_isnan浮動小数点数のバイナリ表現を直接チェックするため、(一部のシステムでは)の実装が機能します。もちろん、プロセッサはまだ (いくつかの) 実際の計算を行う可能性があります (コンパイラが行う最適化に応じて)。したがって、実際の NaN がメモリに表示される可能性があり、そのバイナリ表現を確認できますが、上記で説明したようにstd::isnan、定数falsesqrt同様に、コンパイラが を、 input に対して NaN を生成しないバージョンに置き換えることも同様に起こります-1。コンパイラがどの最適化を行っているかを確認するには、アセンブラにコンパイルしてそのコードを調べます。

(完全に無関係ではない) 類推を行うために、コードが C++ であるとコンパイラーに伝えている場合、C コードを正しくコンパイルすることは期待できず、その逆も同様です (これには実際の例があります。 C と C++ の両方で、各言語でコンパイルすると異なる動作が発生しますか? )。

-ffast-math有効にして使用するのは悪い考えですmy_isnan。これにより、すべてが非常にマシンおよびコンパイラに依存するようになり、コンパイラが全体的にどのような最適化を行うかわからないため、非有限の数学ですが、そうでないことをコンパイラに伝えます。

簡単な修正は、-ffast-math -fno-finite-math-onlyまだいくつかの最適化を提供するものを使用することです。

コードが次のようになっている可能性もあります。

  1. すべての無限大と NaN を除外します
  2. フィルタリングされた値に対していくつかの有限計算を実行します(これは、無限大または NaN を作成しないことが保証されている計算を意味します。これは非常に慎重にチェックする必要があります)

この場合、コードを分割し、optimize#pragmaまたは(それぞれおよび) を使用して、特定のコード部分に対して選択的にオンとオフ__attribute__を切り替えることができます (ただし、これに関連する GCC の一部のバージョンで問題が発生したことを覚えています)。コードを別々のファイルに分割し、異なるフラグでコンパイルするだけです。もちろん、これは、無限大と NaN が発生する可能性のある部分を分離できれば、より一般的な設定でも機能します。これらの部分を分離できない場合は、このコードに使用できないことを強く示しています。-ffast-math-ffinite-math-only-fno-finite-math-only-ffinite-math-only

-ffast-math最後に、これは単にプログラムを高速化する無害な最適化ではないことを理解することが重要です。それはコードのパフォーマンスだけでなく、その正確性にも影響を与えます (そして、浮動小数点数を取り巻くすべての問題に加えて、私の記憶が正しければ、William Kahanは彼のホームページにホラー ストーリーのコレクションを持っています。浮動小数点演算について知っておく必要があります)。つまり、より高速なコードが得られる可能性がありますが、間違った結果や予期しない結果が得られる可能性があります (例については以下を参照してください)。したがって、このような最適化は、自分が何をしているのかを本当に理解している場合にのみ使用してください。

  1. 最適化がその特定のコードの正確さに影響しない、または
  2. 最適化によって導入されたエラーは、コードにとって重要ではありません。

プログラム コードは、この最適化を使用するかどうかによって、実際にはまったく異なる動作をする可能性があります。-ffast-math特に、 などの最適化が有効になっていると、間違った動作をする (または少なくとも期待に反する)可能性があります。たとえば、次のプログラムを見てください。

#include <iostream>
#include <limits>

int main() {
  double d = 1.0;
  double max = std::numeric_limits<double>::max();
  d /= max;
  d *= max;
  std::cout << d << std::endl;
  return 0;
}

1最適化フラグなしでコンパイルすると、期待どおりの出力が生成されますが、 を使用-ffast-mathすると、 が出力されます0

于 2014-04-08T08:17:36.603 に答える