isnan() 関数はありますか?
PS .: 私はMinGWにいます (それが違いを生む場合)。
これは、最初はing していた に<math.h>
は存在しないisnan() from を使用して解決しました。<cmath>
#include
IEEE 標準によると、NaN 値には、それらを含む比較が常にfalse になるという奇妙な特性があります。つまり、float f の場合、f が NaN の場合にのみf != f
true になります。
以下のいくつかのコメントが指摘しているように、コードを最適化するときにすべてのコンパイラがこれを尊重するわけではないことに注意してください。
IEEE 浮動小数点を使用すると主張するコンパイラでは、このトリックが機能するはずです。しかし、それが実際に機能することを保証することはできません。疑問がある場合は、コンパイラに確認してください。
isnan()
現在のC++標準ライブラリで使用できる関数はありません。これはC99で導入され、関数ではなくマクロとして定義されました。C99で定義されている標準ライブラリの要素は、現在のC++標準ISO/ IEC 14882:1998の一部ではなく、その更新ISO / IEC 14882:2003でもありません。
2005年にテクニカルレポート1が提案されました。TR1は、C99との互換性をC++にもたらします。C ++標準になることが正式に採用されたことはありませんが、多くの(GCC4.0 +またはVisualC ++ 9.0+ C ++実装はTR1機能を提供しますが、すべてまたは一部のみを提供します(Visual C ++ 9.0はC99数学関数を提供しません) 。
TR1が利用可能な場合、、などのcmath
C99要素が含まれますが、多くの実装(LinuxのGCC4+またはMacOSX 10.5+のXCode)がそれらを挿入しますが、通常は名前空間でマクロではなく関数として定義されます。に直接、したがって明確に定義されています。isnan()
isfinite()
std::tr1::
std::
std::isnan
さらに、C ++の一部の実装では、C99isnan()
マクロをC ++で使用できるようになっています(cmath
またはを介して含まれますmath.h
)。これにより、さらに混乱が生じ、開発者はそれが標準の動作であると見なす可能性があります。
Viusal C ++に関する注意事項は、前述のとおり、std::isnan
どちらも提供していませんが、 Visual C++6.0以降で使用可能になっているとstd::tr1::isnan
定義された拡張関数を提供します。_isnan()
XCodeには、さらに楽しい機能があります。前述のように、GCC4+はを定義しますstd::isnan
。古いバージョンのコンパイラとライブラリ形式のXCodeの場合、IntelとPower PCの2つの関数が定義されているようです(ここで関連する議論があります)。__inline_isnand()
__isnand()
「公式」の方法には、posixisnan
マクロ、c++0xisnan
関数テンプレート、またはビジュアル c++_isnan
関数の 3 つがあります。
残念ながら、それらのどれを使用するかを検出するのは実際的ではありません。
残念ながら、NaN を使用した IEEE 754 表現を使用しているかどうかを検出する信頼できる方法はありません。標準ライブラリは公式のそのような方法を提供します ( numeric_limits<double>::is_iec559
)。しかし実際には、g++ などのコンパイラはそれを台無しにします。
理論的には単純x != x
に を使用できますが、g++ やビジュアル c++ などのコンパイラはそれを台無しにします。
最終的には、IEEE 754 などの特定の表現を想定して (そして、ある時点で強制することを願っています!)、特定のNaN bitpatternsをテストします。
編集:「g ++などのコンパイラ…それを台無しにする」の例として、考慮してください
#include <limits>
#include <assert.h>
void foo( double a, double b )
{
assert( a != b );
}
int main()
{
typedef std::numeric_limits<double> Info;
double const nan1 = Info::quiet_NaN();
double const nan2 = Info::quiet_NaN();
foo( nan1, nan2 );
}
g++ (TDM-2 mingw32) 4.4.1 でコンパイル:
C:\test> 「C:\Program Files\@commands\gnuc.bat」と入力します @rem -finput-charset=windows-1252 @g++ -O -pedantic -std=c++98 -Wall -Wwrite-strings %* -Wno-long-long C:\test> gnuc x.cpp C:\test> && エコーは動作します... || エコー!失敗 作品... C:\test> gnuc x.cpp --fast-math C:\test> && エコーは動作します... || エコー!失敗 アサーションに失敗しました: a != b、ファイル x.cpp、6 行目 このアプリケーションは、異常な方法で終了するようランタイムに要求しました。 詳細については、アプリケーションのサポート チームにお問い合わせください。 !失敗した C:\テスト> _
コンパイラが c99 拡張機能をサポートしている場合は std::isnan がありますが、mingw がサポートしているかどうかはわかりません。
コンパイラに標準関数がない場合に機能する小さな関数を次に示します。
bool custom_isnan(double var)
{
volatile double d = var;
return d != d;
}
関数を使用できますがisnan()
、C 数学ライブラリを含める必要があります。
#include <cmath>
この関数は C99 の一部であるため、どこでも使用できるわけではありません。ベンダーが関数を提供していない場合は、互換性のために独自のバリアントを定義することもできます。
inline bool isnan(double x) {
return x != x;
}
次のコードでは、NAN の定義 (すべての指数ビット セット、少なくとも 1 つの小数ビット セット) を使用し、sizeof(int) = sizeof(float) = 4 と仮定しています。詳細については、Wikipedia で NAN を検索できます。
bool IsNan( float value )
{
return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000;
}
この質問に対する私の答えは、の遡及チェックを使用しないことですnan
。代わりに、フォームの分割に対して予防チェックを使用してください。0.0/0.0
#include <float.h>
float x=0.f ; // I'm gonna divide by x!
if( !x ) // Wait! Let me check if x is 0
x = FLT_MIN ; // oh, since x was 0, i'll just make it really small instead.
float y = 0.f / x ; // whew, `nan` didn't appear.
nan
操作の結果0.f/0.f
、または0.0/0.0
。 これはコードの安定性にとって恐ろしい宿敵であり、非常に慎重に検出して防止するnan
必要があります1。そのプロパティは、通常の数値とは異なります。nan
nan
有毒です (5* nan
= nan
)nan
それ自体ではなく、何とも等しくありません ( nan
!= nan
)nan
より大きくない ( nan
!> 0)nan
より小さくない ( nan
!< 0)リストされている最後の 2 つのプロパティは反論理的であり、nan
数値との比較に依存するコードの奇妙な動作を引き起こします (最後の 3 番目のプロパティも奇妙ですが、おそらくx != x ?
コードで表示されることはありません (確認している場合を除く)。ナンの場合(信頼できない)))。
私自身のコードでは、nan
値が見つけにくいバグを引き起こす傾向があることに気付きました。(これがorの場合に当てはまらないことに注意してください。( < 0) は を返し、( 0 < ) は TRUE を返し、( < ) は TRUE を返します。そのため、私の経験では、コードの動作は多くの場合、依然として望ましいものです)。inf
-inf
-inf
TRUE
inf
-inf
inf
発生させたいこと0.0/0.0
は特別なケースとして処理する必要がありますが、何を行うかは、コードから得られると予想される数値に依存する必要があります。
0.f/FLT_MIN
上記の例では、( )の結果は0
基本的に になります。代わり0.0/0.0
に生成することもできます。HUGE
そう、
float x=0.f, y=0.f, z;
if( !x && !y ) // 0.f/0.f case
z = FLT_MAX ; // biggest float possible
else
z = y/x ; // regular division.
したがって、上記の場合、 x が0.f
である場合、inf
結果が得られます (実際には、上記のように非常に優れた/非破壊的な動作をします)。
整数を 0 で除算すると実行時例外が発生することに注意してください。したがって、0 による整数除算を常にチェックする必要が0.0/0.0
ありnan
ます0.0/0.0
。
1 viaのチェックは信頼できない場合があります (特にスイッチが有効な場合、IEEE 準拠を破る一部の最適化コンパイラによって取り除かれます)。nan
x != x
x != x
-ffast-math
(x != x) が NaN に対して常に保証されているとは限らないことを考慮して (-ffast-math オプションを使用する場合など)、私は以下を使用しています:
#define IS_NAN(x) (((x) < 0) == ((x) >= 0))
数値は < 0 と >= 0 の両方であってはならないため、実際には、このチェックは、数値がゼロ以下でもゼロ以上でもない場合にのみパスします。これは基本的にまったく数がない、または NaN です。
必要に応じて、これを使用することもできます。
#define IS_NAN(x) (!((x)<0) && !((x)>=0)
ただし、これが -ffast-math の影響を受けるかどうかはわかりません。そのため、走行距離は異なる場合があります。
使用される NaN の特定の IEEE 表現に依存しない可能な解決策は次のとおりです。
template<class T>
bool isnan( T f ) {
T _nan = (T)0.0/(T)0.0;
return 0 == memcmp( (void*)&f, (void*)&_nan, sizeof(T) );
}
これは機能します:
#include <iostream>
#include <math.h>
using namespace std;
int main ()
{
char ch='a';
double val = nan(&ch);
if(isnan(val))
cout << "isnan" << endl;
return 0;
}
出力: イスナン
私にとっては、解決策は、明示的にインライン化して十分に高速にするためのマクロである可能性があります。また、任意のフロート タイプでも機能します。これは、値がそれ自体と等しくない唯一のケースは、値が数値でない場合であるという事実に基づいています。
#ifndef isnan
#define isnan(a) (a != a)
#endif
IEEE 標準では、指数がすべて1
s で仮数が 0 でない場合、数値はNaN
. Double は1
、符号ビット、11
指数ビット、および52
仮数ビットです。少しチェックしてください。
上記のコメントのように、 a != a は g++ やその他のコンパイラでは機能しませんが、このトリックは機能するはずです。それほど効率的ではないかもしれませんが、それでも方法は次のとおりです。
bool IsNan(float a)
{
char s[4];
sprintf(s, "%.3f", a);
if (s[0]=='n') return true;
else return false;
}
基本的に、g++ では (他についてはわかりませんが)、printf は、変数が有効な整数/浮動小数点数でない場合、%d または %.f 形式で「nan」を出力します。したがって、このコードは文字列の最初の文字が「n」(「nan」など) であることを確認しています。