C ++でdouble(またはfloat)がNaNかどうかを確認する


368

isnan()関数はありますか?

PS .:私はMinGWにいます(違いがある場合)。

私は、これは()からISNANを使用することによって解決していた<math.h>に存在しない、<cmath>私がした、#include最初にする。


2
私はあなたがそれを携帯できるようにすることはできません。C ++にはIEEE754が必要だと誰が言っていますか?
David Heffernan


ほんの一言ですが、1オンスの予防は1ポンドの治療より優れています。言い換えると、0.f / 0.fが実行されないようにすることnanは、コード内のを遡及的にチェックするよりもはるかに優れています。nanこれは、プログラムをひどく破壊する可能性があります。増殖を許可すると、バグの発見が難しくなる可能性があります。これは、nan毒性がある(5 * nan= nan)、nan何にも等しくない(nan!= nan)、nan何よりも大きくない(nan!> 0)、nan何よりも小さくない(nan!<0)ためです。
bobobobo 2013

1
@bobobobo:これは一元化されたエラーチェックを可能にする機能です。例外対戻り値のように。
Ben Voigt、2015年

2
<cmath>にisnan()がないのはなぜですか?std ::
frankliuao 2017

回答:


349

IEEE標準によれば、NaN値には、それらを含む比較が常に falseであるという奇妙な特性があります。つまり、float fの場合、fがNaNの場合にのみf != f trueになります。

以下のコメントで指摘されているように、すべてのコンパイラがコードを最適化するときにこれを尊重するわけではないことに注意してください。

IEEE浮動小数点を使用すると主張するコンパイラーの場合、このトリック機能するはずです。しかし、それ実際に機能すること保証することできません。疑わしい場合は、コンパイラに確認してください。


4
コンパイラーは、IEEEモードで実行している場合、これを削除しない方がよいでしょう。もちろん、ご使用のコンパイラのドキュメントを確認してください...
dmckee --- ex-moderator kitten

38
-1は理論的にのみ機能し、実際には機能しません。g++(-fastmathを使用)などのコンパイラはそれを台無しにします。c ++ 0xまでの唯一の一般的な方法は、ビットパターンをテストすることです。
乾杯とhth。-Alf

66
@Alf:-ffast-mathオプションのドキュメントには、数学関数のIEEEまたはISOのルール/仕様の場合、正確な実装に依存するプログラムの出力が正しくなくなる可能性があることが明示されています。そのオプションを有効にしないと、使用x != xはNaNをテストするための完全に有効で移植可能な方法です。
Adam Rosenfield、2011年

7
@Adam:ドキュメントには、非準拠であると公然と述べています、はい。はい、私は以前にその議論に遭遇しました。これについてGabriel Dos Reisと詳しく話し合いました。これは、循環論争でデザインを守るために一般的に使用されます(それに関連付けるつもりだったかどうかはわかりませんが、知っておく価値があります-炎上問題です)。x != xそのオプションなしで有効であるというあなたの結論は論理的には従いません。特定のバージョンのg ++​​の場合もそうでない場合もあります。とにかく、一般に、fastmathオプションが使用されないことを保証する方法はありません。
乾杯とhth。-Alf

7
@アルフ:いいえ、ガブリエルドスレイスとのあなたの話し合いを知りませんでした。スティーブジェソップは、IEEEの代表を引き受けることについての他の質問で素晴らしいポイントを作りました。IEEE 754を想定していて、コンパイラが準拠した方法で動作している(つまり、-ffast-mathオプション)場合、それx != xは有効で移植可能なソリューションです。マクロをテストし-ffast-mathてテストし、__FAST_MATH__その場合は別の実装に切り替えることもできます(たとえば、共用体とビットtwiddlingを使用します)。
Adam Rosenfield、2011年

220

isnan()現在のC ++標準ライブラリで使用できる関数はありません。これはC99で導入され、関数ではなくマクロとして定義されました。C99で定義されている標準ライブラリの要素は、現在のC ++標準ISO / IEC 14882:1998にもその更新ISO / IEC 14882:2003にも含まれていません。

2005年にテクニカルレポート1が提案されました。TR1は、C99との互換性をC ++にもたらします。C ++標準になるために正式に採用されたことはないという事実にもかかわらず、多く(GCC 4.0+またはVisual C ++ 9.0+ C ++実装はTR1機能を提供しますが、そのすべてまたは一部のみ(Visual C ++ 9.0はC99数学関数を提供しません) 。

TR1が利用可能である場合には、cmathのようなC99の要素が含まisnan()isfinite()通常では、などを、彼らは機能ではなく、マクロとして定義されているstd::tr1::それらを注入する(Mac OS X 10.5+上すなわちGCC 4+ Linux上またはXcodeで)多くの実装が、名前空間に直接std::std::isnan明確に定義されています。

さらに、C ++の一部の実装では、C99 isnan()マクロをC ++(cmathまたはを介してインクルードmath.h)で使用できるようにしており、混乱を招く可能性があり、開発者はそれが標準の動作であると想定する場合があります。

上記のように、Viusal C ++に関する注意事項は、std::isnanどちらも提供していませんstd::tr1::isnan_isnan()Visual C ++ 6.0以降で利用可能であると定義されている拡張関数を提供しています。

XCodeでは、さらに楽しいものがあります。前述のように、GCC 4+はを定義しますstd::isnan。古いバージョンのコンパイラーとライブラリー形式のXCodeの場合、それは(ここに関連する議論があります))、自分で確認する機会がなかったようです)__inline_isnand()Intelと__isnand()Power PCの2つの関数が定義されています。


21
誰もがisNanやisInfinityのようなこれらの関数を望んでいます。なぜ担当者は単に標準に含めないのですか???? -私は責任を負う方法を見つけてこれに私の投票を入れようとします。真剣に。
shuhalo

8
@shuhaloまだ担当?
トマーシュZato -復活モニカ

11
この回答std::isnanは現在C ++ 11標準の一部であり、サポートが拡大しているため、更新する必要があります。std :: isnanはVisual Studio 2013以降、Visual Studioに実装されました。たぶん@shuhaloが担当しました:-)
aberaud

170

最初の解決策:C ++ 11を使用している場合

これが尋ねられたので、少し新しい開発がありました:それがstd::isnan()C ++ 11の一部であることを知ることは重要です

あらすじ

ヘッダーで定義 <cmath>

bool isnan( float arg ); (since C++11)
bool isnan( double arg ); (since C++11)
bool isnan( long double arg ); (since C++11)

指定された浮動小数点数argが非数(NaN)かどうかを判別します。

パラメーター

arg:浮動小数点値

戻り値

trueargがの場合NaNfalseそれ以外の場合

参照

http://en.cppreference.com/w/cpp/numeric/math/isnan

これは、g ++を使用する場合、-fast-mathと互換性がないことに注意してください。他の提案については、以下を参照してください。


その他の解決策:C ++ 11に準拠していないツールを使用している場合

C99の場合、Cでは、これはisnan(c)int値を返すマクロとして実装されます。のタイプxは、float、double、またはlong doubleです。

さまざまなベンダーが機能を含む場合と含まない場合がありますisnan()

チェックするはずのポータブルな方法は、NaNIEEE 754プロパティを使用することでNaN、それ自体に等しいではありませんが:つまりx == xはfalseになりますxものNaN

ただし、最後のオプションはすべてのコンパイラと一部の設定(特に最適化設定)では機能しない場合があるため、最後の手段として、ビットパターンをいつでも確認できます...


8
間違いなく受け入れられた回答になるに値し、より多くの賛成票を投じる価値があります。ヒントをありがとう
LBes

3
-1 std::isnanは、g ++の浮動小数点最適化では機能しないため、2017年2月の時点ではまだお勧めできません。
乾杯とhth。-アルフ

@ Cheersandhth.-Alf:このオプションはIEEE準拠ですか?回答が編集されました
BlueTrin

@BlueTrin:x != xisnanは、IEEE 754に準拠するために必要です。後者に関して、IEEE 754-2008規格は、「実装は、サポートされているすべての算術フォーマットに対して次の非計算操作を提供する」と述べており、「isNaN(x)は、xがNaNである場合にのみ真である」と述べています。C ++が代わりに提供する規格が必要is754version1985()とする適合性をチェックするため(IEC 559は同じ規格です)。残念ながら、最適化では、g ++は適合を主張しますが、非適合です。is754version2008()std::numeric_limits<Fp>::is_iec559()-ffast-math
乾杯とhth。-アルフ

1
警告:isnan(x)は、gccおよびclangのオプション-ffinite-math-onlyでは機能しません

82

Boostにはヘッダーのみのライブラリもあり、浮動小数点データ型を処理するための優れたツールがあります

#include <boost/math/special_functions/fpclassify.hpp>

次の関数を取得します。

template <class T> bool isfinite(T z);
template <class T> bool isinf(T t);
template <class T> bool isnan(T t);
template <class T> bool isnormal(T t);

時間があれば、BoostのMathツールキット全体を見てください。多くの便利なツールがあり、急速に成長しています。

また、浮動小数点および非浮動小数点を処理する場合は、数値変換を確認することをお勧めします。


1
ありがとう!まさに私が探していたもの。
ワトソン博士2011

これはBoost 1.35で追加されました(プログラムが古いLinuxディストリビューションでコンパイルできないことがわかりました)。
marcin 2012年

2
オプション--fast-mathを指定してコンパイルすると、この関数は期待どおりに機能しません。
Gaetano Mendola

43

3つの「公式」の方法があります:posix isnanマクロ、c ++ 0x isnan関数テンプレート、またはビジュアルc ++ _isnan関数

残念ながら、どれを使用するかを検出することは実際的ではありません。

残念ながら、NaNを使用したIEEE 754表現があるかどうかを検出する信頼できる方法はありません。標準ライブラリは公式にそのような方法を提供します(numeric_limits<double>::is_iec559)。しかし実際には、g ++などのコンパイラはそれを台無しにしています。

理論的には単純x != xにを使用できますが、g ++やビジュアルc ++などのコンパイラはそれを台無しにします。

したがって、最後に、特定のNaNビットパターンをテストし、IEEE 754などの特定の表現を想定します(ある時点で強制することをお勧めします!)。


編集:「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:\ test> _

4
@Alf:あなたの例は、Mac OS XとLinuxの両方で、4.0と4.5の間のg ++​​のさまざまなバージョンで期待どおりに機能します。-ffast-mathオプションのドキュメントには、数学関数のIEEEまたはISOルール/仕様の場合、正確な実装に依存するプログラムの出力が正しくなくなる可能性があることが明示されています。そのオプションを有効にしないと、使用x != xはNaNをテストするための完全に有効で移植可能な方法です。
Adam Rosenfield、2011年

6
@Adam:不足しているのは、C ++標準では、浮動小数点のIEEE表現または数学が必要ないことです。マンページが伝える限り、それgcc -ffast-mathはまだ準拠しているC ++実装です(まあ、それがnumeric_limits::is_iec559正しいと仮定すると、そうですが、Alfは上でそうではないと示唆しています):IEEEに依存するC ++コード移植可能なC ++ではなく、権利がありません実装がそれを提供することを期待します。
Steve Jessop、2011年

5
そしてアルフの権利、GCC 4.3.4とのクイックテストis_iec559で真です-ffast-math。ここでの問題は、のためにGCCのドキュメントということですので、-ffast-math唯一、彼らは一方で、それは、数学関数のための非IEEE / ISOだと言うべきで、その実装はので、それは、非C ++だと言うnumeric_limitsborkedされます。GCCは、テンプレートが定義されているときに、最終的なバックエンドが実際に準拠するフロートを持っているかどうかを常に通知できるとは限らないので、試行することすらありません。IIRC GCCのC99準拠に関する未解決のバグリストには、同様の問題があります。
Steve Jessop、2011年

1
@ Alf、@ Steve、C ++標準に浮動小数点値に関する仕様がないことを知りませんでした。それは私にはかなり衝撃的です。IEEE 754およびNaNを、標準ではなくプラットフォーム固有の拡張として扱う方が見栄えがよくなります。だよね?また、C ++ 0xに追加されたあらゆる種類のisnan()またはIEEE754を期待できますか?
Eonil、2011年

3
@Eonil:C ++ 0xには、たとえば、「浮動小数点型の値の表現は実装によって定義されています」とあります。CとC ++はどちらも、浮動小数点ハードウェアのないマシンでの実装をサポートすることを目的としており、適切なIEEE 754浮動小数点数は、適度に正確な代替手段よりもエミュレートがかなり遅い場合があります。理論ではis_iec559、実際にはGCCで動作しないように見えるIEEEが必要かどうかを断言できます。C ++ 0xにはisnan関数がありますが、GCCはis_iec559現在正しく実装されていないため、C ++ 0xにも実装されず、-ffast-mathが壊れる可能性がありますisnan
Steve Jessop、2011年

39

コンパイラがc99拡張をサポートしている場合はstd :: isnanがありますが、mingwがサポートしているかどうかはわかりません。

コンパイラに標準関数がない場合に機能する小さな関数を次に示します。

bool custom_isnan(double var)
{
    volatile double d = var;
    return d != d;
}

6
なぜvar!= varだけではないのですか?
ブライアンR.ボンディ

8
その場合、コンパイラーは比較を最適化して、常にtrueを返すチャンスです。
CTT、

23
いいえ、ありません。それを行うコンパイラは壊れています。標準ライブラリisnanが誤った結果を返す可能性があると言うのもよいでしょう。技術的には真実ですが、コンパイラにバグがある可能性がありますが、実際にはそうではありません。と同じvar != var。これは、IEEE浮動小数点値の定義方法です。
2010年

29
-ffast-mathが設定されている場合、isnan()はgccの正しい結果を返しません。もちろん、この最適化はIEEEの意味論を壊すものとして文書化されています...
Matthew Herrmann

-ffast-mathが設定されている場合、コンパイラはバグがあります。または、-ffast-mathが設定されている場合、すべてのベットがオフになり、とにかくNaNに依存することはできません。
Adrian Ratnapala

25

標準ライブラリでnumeric_limits<float>::quiet_NaN( )定義されてlimitsいるを使用してテストできます。には別の定数が定義されていdoubleます。

#include <iostream>
#include <math.h>
#include <limits>

using namespace std;

int main( )
{
   cout << "The quiet NaN for type float is:  "
        << numeric_limits<float>::quiet_NaN( )
        << endl;

   float f_nan = numeric_limits<float>::quiet_NaN();

   if( isnan(f_nan) )
   {
       cout << "Float was Not a Number: " << f_nan << endl;
   }

   return 0;
}

Linux上のg ++​​でのみテストしたため、これがすべてのプラットフォームで機能するかどうかはわかりません。


2
ただし、注意してください。GCCバージョン3.2.3では、quiet_NaNに対して0.0を返すため、numeric_limitsにバグがあるようです。私の経験では、GCCの以降のバージョンで問題ありません。
Nathan Kitchen

@ネイサン:知っておくと良い。私はバージョン4.3.2を使用しているので、森の外にいます。
リザードを請求する

18

isnan()関数を使用できますが、C数学ライブラリを含める必要があります。

#include <cmath>

この関数はC99の一部であるため、どこでも使用できるわけではありません。ベンダーが関数を提供していない場合は、互換性のために独自のバリアントを定義することもできます。

inline bool isnan(double x) {
    return x != x;
}

私は<cmath>を使っていましたが、その中にisnanはありません!ちなみに私はそこにいることが分かったですisnanで<math.h>の
HASEN

1
私が言ったように、これはC99の一部です。C99は現在のC ++標準の一部ではないため、代替手段を提供しました。しかし、isnan()が次のC ++標準に含まれる可能性があるため、#ifndefディレクティブをその周りに配置します。
raimue 2009

12

次のコードは、NAN(すべての指数ビットセット、少なくとも1つの小数ビットセット)の定義を使用し、sizeof(int)= sizeof(float)= 4と想定しています。詳細については、ウィキペディアでNANを検索できます。

bool IsNan( float value ) { return ((*(UINT*)&value) & 0x7fffffff) > 0x7f800000; }


これはビッグエンディアンのプラットフォームでも機能すると思います。リテラル0x7fffffffは単にとしてメモリに格納されff ff ff 7fます。 valueの順序はと同じな0x7f800000ので、すべての操作が整列します(バイトのスワップはありません)。誰かがビッグエンディアンのプラットフォームでこれをテストできるかどうか興味があります。
ブライアンW.ワグナー

0x7fff1234NaNでもあります。そうです0xffffffff
スティーブホラッシュ

12

ナン防止

この質問に対する私の答えは、の遡及チェックを使用しないことです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-infTRUEinf-infinf

ナンの下で何をすべきか

発生したいこと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.finf結果として(実際に上記のように、かなり良好な/非破壊的な動作になります)。

覚えておいて、0による整数除算は、実行時例外が発生。そのため、常に0による整数除算をチェックする必要があります。0.0/0.0静かに評価するからnanといって、怠惰であり、0.0/0.0それが起こる前にチェックしない可能性があるわけではありません。

1 viaのチェックは信頼できない場合があります(特にスイッチが有効になっている場合、IEEEコンプライアンスに違反するいくつかの最適化コンパイラによって取り除かれます)。nanx != xx != x-ffast-math


これを指摘してくれてありがとう。そのようなプログラミングは間違いなくそのような問題に役立ちます。ただし、次回は、テキストの書式設定機能を過度に悪用しないようにしてください。このようにフォントサイズ、太さ、スタイルを切り替えると、読みにくくなります。
Magnus

4
0.0 / 0.0がNaNになる可能性がある唯一の演算ではないことに注意してください。負の数の平方根はNaNを返します。+ infinityの余弦はNaNも返します。xが[0、pi]の範囲にない場合の演算acos(x)もNaNになる可能性があります。簡単に言えば、0.0 / 0.0だけでなく、これらの潜在的に危険な操作も注意深く見なければなりません。
Boris Dalstein 2014年

ボリスに完全に同意します。私の経験では、NaNは事実上常にsqrt(-1.302e-53)のようなものから得られました。つまり、ゼロに近い中間計算結果は、否定性をチェックせずにsqrtに送られます。
hans_meine 2016

1
「NaNの防止」とは、除算だけでなく、すべての基本的な算術演算の内部に入る必要があることを意味します。とりわけ、∞/∞、0 *∞、∞%x、x%0、∞-∞、0 ^ 0、∞^ 0に注意する必要があります。このような基本的な算術演算で「予防的」であることは、完全にパフォーマンスを低下させることになる(そして、考えていなかった追加のケースを見逃す可能性が高い)ことを意味します。
Steve Hollasch

11

C ++ 14以降、浮動小数点数valueがNaN かどうかをテストする方法はいくつかあります。

これらの方法のうち、私の元の回答で述べたように、数値表現のビットチェックのみが確実に機能します。特に、std::isnan頻繁に提案されるチェックv != vは確実に機能しないため、使用しないでください。浮動小数点の最適化が必要であると誰かが判断してコンパイラーに実行を依頼したときにコードが正しく機能しないようにしてください。この状況は変化する可能性があり、コンパイラーはより準拠する可能性がありますが、この問題については、最初の回答から6年間は発生していません。

約6年間、私の最初の答えはこの質問の選択された解決策でしたが、問題ありませんでした。しかし最近、信頼性の低いv != vテストを推奨する非常に支持された回答が選択されました。したがって、この追加の最新の回答(C ++ 11とC ++ 14の標準、およびC ++ 17が間もなく登場します)。


C ++ 14以降のNaN性をチェックする主な方法は次のとおりです。

  • std::isnan(value) )
    C ++ 11以降の標準ライブラリの方法です。isnan明らかに同じ名前のPosixマクロと競合しますが、実際にはそれは問題ではありません。主な問題は、浮動小数点算術最適化が要求されたときに、少なくとも1つのメインコンパイラ、つまりg ++を使用std::isnan するとfalse、NaN引数が返されることです。

  • (fpclassify(value) == FP_NAN) )
    と同じ問題に苦しんでいるstd::isnan、つまり、信頼できない。

  • (value != value) )
    多くのSO回答で推奨されています。と同じ問題に苦しんでいるstd::isnan、つまり、信頼できない。

  • (value == Fp_info::quiet_NaN()) )
    これは、標準の動作ではNaNを検出しないはずのテストですが、最適化された動作では(ビットレベル表現を直接比較する最適化されたコードにより)NaNを検出できる可能性があり、おそらく標準の最適化されていない動作をカバーする別の方法と組み合わせることができます、確実にNaNを検出できます。残念ながら、それは確実に機能しないことが判明しました。

  • (ilogb(value) == FP_ILOGBNAN) )
    と同じ問題に苦しんでいるstd::isnan、つまり、信頼できない。

  • isunordered(1.2345, value) )
    と同じ問題に苦しんでいるstd::isnan、つまり、信頼できない。

  • is_ieee754_nan( value ) )
    これは標準機能ではありません。IEEE 754標準に従ってビットをチェックしています。完全に信頼できます、コードはシステムに多少依存します。


次の完全なテストコードでは、「成功」は式が値のナンネスを報告するかどうかです。ほとんどの式では、この成功の尺度であるNaNとNaNのみを検出するという目標は、標準のセマンティクスに対応しています。以下のため(value == Fp_info::quiet_NaN()) )の式は、しかし、標準的な動作は、それがNaN、検出器として動作しないということです。

#include <cmath>        // std::isnan, std::fpclassify
#include <iostream>
#include <iomanip>      // std::setw
#include <limits>
#include <limits.h>     // CHAR_BIT
#include <sstream>
#include <stdint.h>     // uint64_t
using namespace std;

#define TEST( x, expr, expected ) \
    [&](){ \
        const auto value = x; \
        const bool result = expr; \
        ostringstream stream; \
        stream << boolalpha << #x " = " << x << ", (" #expr ") = " << result; \
        cout \
            << setw( 60 ) << stream.str() << "  " \
            << (result == expected? "Success" : "FAILED") \
            << endl; \
    }()

#define TEST_ALL_VARIABLES( expression ) \
    TEST( v, expression, true ); \
    TEST( u, expression, false ); \
    TEST( w, expression, false )

using Fp_info = numeric_limits<double>;

inline auto is_ieee754_nan( double const x )
    -> bool
{
    static constexpr bool   is_claimed_ieee754  = Fp_info::is_iec559;
    static constexpr int    n_bits_per_byte     = CHAR_BIT;
    using Byte = unsigned char;

    static_assert( is_claimed_ieee754, "!" );
    static_assert( n_bits_per_byte == 8, "!" );
    static_assert( sizeof( x ) == sizeof( uint64_t ), "!" );

    #ifdef _MSC_VER
        uint64_t const bits = reinterpret_cast<uint64_t const&>( x );
    #else
        Byte bytes[sizeof(x)];
        memcpy( bytes, &x, sizeof( x ) );
        uint64_t int_value;
        memcpy( &int_value, bytes, sizeof( x ) );
        uint64_t const& bits = int_value;
    #endif

    static constexpr uint64_t   sign_mask       = 0x8000000000000000;
    static constexpr uint64_t   exp_mask        = 0x7FF0000000000000;
    static constexpr uint64_t   mantissa_mask   = 0x000FFFFFFFFFFFFF;

    (void) sign_mask;
    return (bits & exp_mask) == exp_mask and (bits & mantissa_mask) != 0;
}

auto main()
    -> int
{
    double const v = Fp_info::quiet_NaN();
    double const u = 3.14;
    double const w = Fp_info::infinity();

    cout << boolalpha << left;
    cout << "Compiler claims IEEE 754 = " << Fp_info::is_iec559 << endl;
    cout << endl;;
    TEST_ALL_VARIABLES( std::isnan(value) );                    cout << endl;
    TEST_ALL_VARIABLES( (fpclassify(value) == FP_NAN) );        cout << endl;
    TEST_ALL_VARIABLES( (value != value) );                     cout << endl;
    TEST_ALL_VARIABLES( (value == Fp_info::quiet_NaN()) );      cout << endl;
    TEST_ALL_VARIABLES( (ilogb(value) == FP_ILOGBNAN) );        cout << endl;
    TEST_ALL_VARIABLES( isunordered(1.2345, value) );           cout << endl;
    TEST_ALL_VARIABLES( is_ieee754_nan( value ) );
}

g ++での結果(再び、 (value == Fp_info::quiet_NaN())、NaN検出器としては機能しないことです。ここでは実際に非常に重要です)。

[C:\ my \ forums \ so \ 282(NaNを検出)]
> g ++ --version | 「++」を見つける
g ++(x86_64-win32-sjlj-rev1、MinGW-W64プロジェクトによってビルド)6.3.0

[C:\ my \ forums \ so \ 282(NaNを検出)]
> g ++ foo.cpp && a
コンパイラはIEEE 754 = trueと主張している

v = nan、(std :: isnan(value))= true成功
u = 3.14、(std :: isnan(value))= false成功
w = inf、(std :: isnan(value))= false成功

v = nan、((fpclassify(value)== 0x0100))= true成功
u = 3.14、((fpclassify(value)== 0x0100))= false成功
w = inf、((fpclassify(value)== 0x0100))= false成功

v = nan、((value!= value))= true成功
u = 3.14、((value!= value))= false成功
w = inf、((value!= value))= false成功

v = nan、((value == Fp_info :: quiet_NaN()))= false失敗
u = 3.14、((value == Fp_info :: quiet_NaN()))= false成功
w = inf、((value == Fp_info :: quiet_NaN()))= false成功

v = nan、((ilogb(value)==((int)0x80000000)))= true成功
u = 3.14、((ilogb(value)==((int)0x80000000)))= false成功
w = inf、((ilogb(value)==((int)0x80000000)))= false成功

v = nan、(isunordered(1.2345、value))= true成功
u = 3.14、(isunordered(1.2345、value))= false成功
w = inf、(isunordered(1.2345、value))= false成功

v = nan、(is_ieee754_nan(value))= true成功
u = 3.14、(is_ieee754_nan(value))= false成功
w = inf、(is_ieee754_nan(value))= false成功

[C:\ my \ forums \ so \ 282(NaNを検出)]
> g ++ foo.cpp -ffast-math && a
コンパイラはIEEE 754 = trueと主張している

v = nan、(std :: isnan(value))= false失敗
u = 3.14、(std :: isnan(value))= false成功
w = inf、(std :: isnan(value))= false成功

v = nan、((fpclassify(value)== 0x0100))= false失敗
u = 3.14、((fpclassify(value)== 0x0100))= false成功
w = inf、((fpclassify(value)== 0x0100))= false成功

v = nan、((value!= value))= false失敗
u = 3.14、((value!= value))= false成功
w = inf、((value!= value))= false成功

v = nan、((value == Fp_info :: quiet_NaN()))= true成功
u = 3.14、((value == Fp_info :: quiet_NaN()))= true失敗
w = inf、((value == Fp_info :: quiet_NaN()))= true失敗

v = nan、((ilogb(value)==((int)0x80000000)))= true成功
u = 3.14、((ilogb(value)==((int)0x80000000)))= false成功
w = inf、((ilogb(value)==((int)0x80000000)))= false成功

v = nan、(isunordered(1.2345、value))= false失敗
u = 3.14、(isunordered(1.2345、value))= false成功
w = inf、(isunordered(1.2345、value))= false成功

v = nan、(is_ieee754_nan(value))= true成功
u = 3.14、(is_ieee754_nan(value))= false成功
w = inf、(is_ieee754_nan(value))= false成功

[C:\ my \ forums \ so \ 282(NaNを検出)]
> _

Visual C ++での結果:

[C:\ my \ forums \ so \ 282(NaNを検出)]
> cl / nologo- 2>&1 | 「++」を見つける
Microsoft(R)C / C ++ Optimizing Compilerバージョン19.00.23725 for x86

[C:\ my \ forums \ so \ 282(NaNを検出)]
> cl foo.cpp / Feb && b
foo.cpp
コンパイラはIEEE 754 = trueと主張している

v = nan、(std :: isnan(value))= true成功
u = 3.14、(std :: isnan(value))= false成功
w = inf、(std :: isnan(value))= false成功

v = nan、((fpclassify(value)== 2))= true成功
u = 3.14、((fpclassify(value)== 2))= false成功
w = inf、((fpclassify(value)== 2))= false成功

v = nan、((value!= value))= true成功
u = 3.14、((value!= value))= false成功
w = inf、((value!= value))= false成功

v = nan、((value == Fp_info :: quiet_NaN()))= false失敗
u = 3.14、((value == Fp_info :: quiet_NaN()))= false成功
w = inf、((value == Fp_info :: quiet_NaN()))= false成功

v = nan、((ilogb(value)== 0x7fffffff))= true成功
u = 3.14、((ilogb(value)== 0x7fffffff))= false成功
w = inf、((ilogb(value)== 0x7fffffff))= true失敗

v = nan、(isunordered(1.2345、value))= true成功
u = 3.14、(isunordered(1.2345、value))= false成功
w = inf、(isunordered(1.2345、value))= false成功

v = nan、(is_ieee754_nan(value))= true成功
u = 3.14、(is_ieee754_nan(value))= false成功
w = inf、(is_ieee754_nan(value))= false成功

[C:\ my \ forums \ so \ 282(NaNを検出)]
> cl foo.cpp / Feb / fp:fast && b
foo.cpp
コンパイラはIEEE 754 = trueと主張している

v = nan、(std :: isnan(value))= true成功
u = 3.14、(std :: isnan(value))= false成功
w = inf、(std :: isnan(value))= false成功

v = nan、((fpclassify(value)== 2))= true成功
u = 3.14、((fpclassify(value)== 2))= false成功
w = inf、((fpclassify(value)== 2))= false成功

v = nan、((value!= value))= true成功
u = 3.14、((value!= value))= false成功
w = inf、((value!= value))= false成功

v = nan、((value == Fp_info :: quiet_NaN()))= false失敗
u = 3.14、((value == Fp_info :: quiet_NaN()))= false成功
w = inf、((value == Fp_info :: quiet_NaN()))= false成功

v = nan、((ilogb(value)== 0x7fffffff))= true成功
u = 3.14、((ilogb(value)== 0x7fffffff))= false成功
w = inf、((ilogb(value)== 0x7fffffff))= true失敗

v = nan、(isunordered(1.2345、value))= true成功
u = 3.14、(isunordered(1.2345、value))= false成功
w = inf、(isunordered(1.2345、value))= false成功

v = nan、(is_ieee754_nan(value))= true成功
u = 3.14、(is_ieee754_nan(value))= false成功
w = inf、(is_ieee754_nan(value))= false成功

[C:\ my \ forums \ so \ 282(NaNを検出)]
> _

上記の結果をまとめるとis_ieee754_nan、このテストプログラムで定義された関数を使用したビットレベル表現の直接テストのみが、g ++とVisual C ++の両方ですべてのケースで確実に機能しました。


補遺:
私は述べた、の意識はまだNaNのためのテストに別のことが可能になった上に掲示した後、別の答え、すなわち、ここでは((value < 0) == (value >= 0))。これはVisual C ++ではうまく機能することが判明しましたが、g ++の-ffast-mathオプションでは失敗しました。直接ビットパターンテストのみが確実に機能します。


7
inline bool IsNan(float f)
{
    const uint32 u = *(uint32*)&f;
    return (u&0x7F800000) == 0x7F800000 && (u&0x7FFFFF);    // Both NaN and qNan.
}

inline bool IsNan(double d)
{
    const uint64 u = *(uint64*)&d;
    return (u&0x7FF0000000000000ULL) == 0x7FF0000000000000ULL && (u&0xFFFFFFFFFFFFFULL);
}

これsizeof(int)は、4とsizeof(long long)8の場合に機能します。

実行時は単なる比較であり、キャストには時間がかかりません。比較フラグの設定を変更して、等しいかどうかを確認します。


また、IEEE 754表現に限定されています。
乾杯とhth。-アルフ

このキャストはg ++の厳密なエイリアシング規則に違反し、そのコンパイラーは正式なUBを検出したときにUnmentionable Things™を実行することが知られていることに注意してください。効率的なキャストの代わりに、g ++ではmemcpy確実にバイト配列を使用する必要があります。私の#2答えにそのためのコード
乾杯とhth。-アルフ

4

使用される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) );
}

単精度浮動小数点には、NaNに対して800万を超える正当で異なるビット表現があるため、さらに比較を追加する必要があります。:)
Steve Hollasch

4

(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の影響を受けるかどうかはわかりません。そのため、走行距離が異なる場合があります。


これも実際に欠陥があるのと同じ方法f != fで欠陥があります。llvmがほぼ同一のコードを離れて最適化するのを見てきました。オプティマイザは、最初の比較に関する情報を伝達し、最初の比較が真の場合、2番目の比較が真になることはないことを理解できます。(コンパイラがIEEEのルールに厳密に従っている場合は、f != fとにかくはるかに単純です)
Markus

g ++の-ffast-mathオプションでは機能しません。Visual C ++で動作します。(stackoverflow.com/a/42138465/464581)を参照してください。
乾杯とhth。-アルフ

3

私に関しては、解決策はそれを明示的にインライン化して十分に高速にするマクロである可能性があります。また、どのフロートタイプでも機能します。これは、値がそれ自体と等しくない唯一のケースが、値が数値ではないという事実に基づいています。

#ifndef isnan
  #define isnan(a) (a != a)
#endif

これは、この質問に対する最良の回答の1つです。共有してくれてありがとう。
アンリメンケ2013

2
他の回答は、これが-ffast-mathオプションセットで失敗する可能性があることを示しています。
テクノフィル2016年

3

これは機能します:

#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;
}

出力:isnan


1

真に最良のクロスプラットフォームアプローチは、ユニオンを使用し、doubleのビットパターンをテストしてNaNをチェックすることです。

私はこのソリューションを完全にテストしていません。ビットパターンを操作するより効率的な方法があるかもしれませんが、うまくいくと思います。

#include <stdint.h>
#include <stdio.h>

union NaN
{
    uint64_t bits;
    double num;
};

int main()
{
    //Test if a double is NaN
    double d = 0.0 / 0.0;
    union NaN n;
    n.num = d;
    if((n.bits | 0x800FFFFFFFFFFFFF) == 0xFFFFFFFFFFFFFFFF)
    {
        printf("NaN: %f", d);
    }

    return 0;
}

「最近作成されていない組合のメンバーから読み取ることは未定義の動作です」ことに注意してください。したがって、このunion2つのタイプ間の型パンへの使用は、期待どおりに機能しない可能性があります(:sad_panda :)。正しい(実際には、必要なほど移植性は高くありませんが)方法は、ユニオンを完全に回避doubleし、別のuint64_t変数にmemcpyを実行してから、そのヘルパー変数を使用してテストを実行することです。
エルジェイ

0

x86-64では、-ffast-mathコンパイラオプションに関係なく機能するNaNと無限大をチェックするための非常に高速なメソッドを使用できます。(f != fstd::isnanstd::isinf常に得false-ffast-math)。


NaN、無限大、有限数のテストは、最大指数をチェックすることで簡単に行えます。無限大は仮数がゼロの最大指数、NaNは仮数が最大でゼロ以外の仮数です。指数は最上位の符号ビットの次のビットに格納されるため、左シフトして符号ビットを取り除き、指数を最上位のビットにすることができます。マスキング(operator&)は不要です。

static inline uint64_t load_ieee754_rep(double a) {
    uint64_t r;
    static_assert(sizeof r == sizeof a, "Unexpected sizes.");
    std::memcpy(&r, &a, sizeof a); // Generates movq instruction.
    return r;
}

static inline uint32_t load_ieee754_rep(float a) {
    uint32_t r;
    static_assert(sizeof r == sizeof a, "Unexpected sizes.");
    std::memcpy(&r, &a, sizeof a); // Generates movd instruction.
    return r;
}

constexpr uint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);
constexpr uint32_t inf_float_shl1 = UINT32_C(0xff000000);

// The shift left removes the sign bit. The exponent moves into the topmost bits,
// so that plain unsigned comparison is enough.
static inline bool isnan2(double a)    { return load_ieee754_rep(a) << 1  > inf_double_shl1; }
static inline bool isinf2(double a)    { return load_ieee754_rep(a) << 1 == inf_double_shl1; }
static inline bool isfinite2(double a) { return load_ieee754_rep(a) << 1  < inf_double_shl1; }
static inline bool isnan2(float a)     { return load_ieee754_rep(a) << 1  > inf_float_shl1; }
static inline bool isinf2(float a)     { return load_ieee754_rep(a) << 1 == inf_float_shl1; }
static inline bool isfinite2(float a)  { return load_ieee754_rep(a) << 1  < inf_float_shl1; }

stdバージョンisinfisfinite負荷2つdouble/floatの定数.dataセグメントは、最悪の場合のシナリオでは、それらは2つのデータ・キャッシュ・ミスを引き起こす可能性があります。上記のバージョンはデータをロードせずinf_double_shl1inf_float_shl1定数はアセンブリー命令への直接のオペランドとしてエンコードされます。


より高速なのは、isnan22つの組み立て手順です。

bool isnan2(double a) {
    bool r;
    asm(".intel_syntax noprefix"
        "\n\t ucomisd %1, %1"
        "\n\t setp %b0"
        "\n\t .att_syntax prefix"
        : "=g" (r)
        : "x" (a)
        : "cc"
        );
    return r;
}

ucomisd引数がNaNの場合、命令がパリティフラグを設定するという事実を使用します。これはstd::isnan-ffast-mathオプションが指定されていない場合の動作です。


-1

IEEE規格では、指数がすべて1sで仮数がゼロでない場合、数値はであるとされていNaNます。Doubleは、1符号ビット、11指数ビット、および52仮数ビットです。ビットチェックを行います。


-3

上記のコメントにあるように、!= 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」のように)


2
a = 234324.0fの場合、バッファオーバーフローは発生しませんか?
Mazyod 2013

はいt'will、または340282346638528859811704183484516925440.000a =の場合FLT_MAX。彼はを使用する必要がありますchar s[7]; sprintf(s, "%.0g", a);。の場合a=-FLT_MAXは6時間です。または-3e+38
bobobobo 2013

-3

これにより、Visual Studioで無限とNaNが検出されます。

//#include <float.h>
double x, y = -1.1; x = sqrt(y);
if (x >= DBL_MIN && x <= DBL_MAX )
    cout << "DETECTOR-2 of errors FAILS" << endl;
else
    cout << "DETECTOR-2 of errors OK" << endl;

定義を確認しFLT_MINDBL_MINそしてLDBL_MINより慎重に。これらは、各タイプの最小正規化値であると定義されています。たとえば、単精度には、ゼロより大きく、かつより小さいFLT_MIN(そしてNaNではない)800万を超える正当なderrm値があります。
Steve Hollasch
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.