C++でNaNを使用する最良の方法は何ですか?
std::numeric_limits<double>::quiet_NaN()
とstd::numeric_limits<double>::signaling_NaN()
が見つかりました。次のように、signaling_NaN
を使用して、初期化されていない変数を表します。
double diameter = std::numeric_limits<double>::signaling_NaN();
ただし、これは割り当て時にシグナル(例外を発生)します。割り当てではなく使用時に例外を発生させたい。
割り当ての例外を発生させずにsignaling_NaN
を使用する方法はありますか?使用時に浮動小数点例外が発生するsignaling_NaN
の優れた移植可能な代替品はありますか?
もう少し調べてみると、signaling_NaN
は、提供されているとおりに使用できません。浮動小数点例外が有効になっている場合、それを呼び出すと、シグナルNaNの処理としてカウントされるため、すぐに例外が発生します。浮動小数点例外が無効になっている場合、シグナリングNaNを処理すると、自動的にそれがクワイエットNaNに降格されるため、signaling_NaN
はどちらの方法でも機能しません。
Menkboyのコード は機能しますが、シグナリングNaNを使用しようとすると、他の問題が発生します。浮動小数点例外を有効または無効にするポータブルな方法はありません( here および ここで )、そして例外の有効化に依存している場合、サードパーティのコードがそれらを無効にする可能性があります( ここ で説明)。
Mottiのソリューション が本当に最良の選択のようです。
信号NANが意味することは、CPUが信号に遭遇したときに信号が発生するということです(そのため、名前が付けられています)。初期化されていない変数を検出する場合、コンパイラで警告レベルを上げると、通常、初期化されていない値を使用するすべてのパスが検出されます。値が初期化されているかどうかを示すブール値を格納するラッパークラスを使用できない場合:
template <class T>
class initialized {
T t;
bool is_initialized;
public:
initialized() : t(T()), is_initialized(false) { }
initialized(const T& tt) : t(tt), is_initialized(true) { }
T& operator=(const T& tt) { t = tt; is_initialized = true; return t; }
operator T&() {
if (!is_initialized)
throw std::exception("uninitialized");
return t;
}
};
簡単な答え:ヘッダーファイルで次のようなことを行い、それを他のあらゆる場所で使用します。
_#define NegativeNaN log(-1)
_
それらに対して何らかの操作を行いたい場合は、exp()
のようにextended_exp()
の周りに拡張ラッパー関数を記述してください。
まあ、静寂と信号NaNの両方の定義を見ると、私は本当に違いを理解することはできません。
これらの関数で使用されているコードを自分で使用することもできます。それにより、その方法で例外が防止される可能性がありますが、これら2つの関数で例外が表示されない場合は、他の何かに関連している可能性があります。
NaNを直接割り当てる場合:
double value = _Nan._Double;
次のような例外をトリガーすることなく、シグナルNaNを変数に書き込むことができます(nb:untested)
void set_snan( double &d )
{
long long *bits = (long long *)&d;
*bits = 0x7ff0000080000001LL;
}
ほとんどの場所で機能しますが、100%ポータブルではありません。
C++実装には、浮動小数点環境にアクセスして特定の浮動小数点例外をテストしてクリアするためのAPIが含まれている場合があります。詳細は 関連する質問に対する私の回答 を参照してください。