Cでnanとinfを使用する方法は?


89

エラーが発生した場合にnanまたはinfを返すことができる数値メソッドがあります。テスト目的で、状況が正しく処理されていることを確認するために、一時的にnanまたはinfを返すように強制したいと思います。Cでnanとinfの値を作成するための、コンパイラに依存しない信頼できる方法はありますか?

約10分間グーグルした後、コンパイラに依存するソリューションしか見つけることができませんでした。


フロートはC標準では定義されていません。したがって、コンパイラに依存しない方法で目的を実行することはできません。
Johan Kotlinski

回答:


86

実装にそれがあるかどうかをテストできます。

#include <math.h>
#ifdef NAN
/* NAN is supported */
#endif
#ifdef INFINITY
/* INFINITY is supported */
#endif

の存在はINFINITYC99(または少なくとも最新のドラフト)によって保証されており、「正または符号なしの無限大を表すfloat型の定数式に拡張されます。それ以外の場合は、変換時にオーバーフローするfloat型の正の定数に拡張されます」。

NAN 定義される場合とされない場合があり、「実装がfloat型のクワイエットNaNをサポートする場合にのみ定義されます。これは、クワイエットNaNを表すfloat型の定数式に展開されます。」

浮動小数点値を比較する場合は、次のことに注意してください。

a = NAN;

その時でさえ、

a == NAN;

は誤りです。NaNをチェックする1つの方法は、次のとおりです。

#include <math.h>
if (isnan(a)) { ... }

次のこともできます:がNaNであるa != aかどうかをテストしaます。

ありisfinite()isinf()isnormal()、およびsignbit()マクロがでmath.hC99インチ

C99には次のnan機能もあります。

#include <math.h>
double nan(const char *tagp);
float nanf(const char *tagp);
long double nanl(const char *tagp);

(参照:n1256)。

ドキュメントINFINITY ドキュメントNAN


2
素晴らしい答え。C.の適合実装上のNANとINFINITYマクロのリファレンス(ISNAN())のほかC99§7.12パラグラフ4と5です。あなたも(!=)を使用してはNaNをチェックすることができます
スティーブンキヤノン

23
読みやすさを愛するために、絶対に使用しa != aないでください。
Chris Kerekes 2012

1
@ChrisKerekes:悲しいことに、私たちの中にはNANを持っているが、isnan()を持っていない人もいます。はい、これは2017年です。:(
eff

Cはa、が数値でない場合、a == NANfalseを返す必要はありません。IEEEはそれを要求します。IEEEに準拠する実装でさえ、ほとんどの場合そうしますisnan()実装されていない場合は、直接コーディングするよりもテストをラップする方がよいでしょうa == NAN
chux -復活モニカ

34

C(またはC ++)標準では、浮動小数点演算タイプがNANまたはINFをサポートする必要があるとは規定されていないため、これを行うコンパイラに依存しない方法はありません。

編集: C ++標準の文言を確認したところ、これらの関数(テンプレート化されたクラスnumeric_limitsのメンバー)は次のようになっています。

quiet_NaN() 
signalling_NaN()

「利用可能な場合」、NAN表現を返します。「利用可能な場合」の意味は拡張されませんが、おそらく「実装のFP担当者がそれらをサポートしている場合」のようなものです。同様に、次の関数があります。

infinity() 

これは、「利用可能な場合」に正のINF担当者を返します。

これらは両方とも<limits>ヘッダーで定義されています-C標準には似たようなものがあると思いますが(おそらく「利用可能な場合」も)、現在のC99標準のコピーはありません。


それは残念で驚くべきことです。CとC ++は、nanとinfの標準表現を持つIEEE浮動小数点数に準拠していませんか?
グラフィックスヌーブ

14
C99、Cヘッダにおいて<math.h>定義nan()nanf()およびnanl()NaNでのそのリターン異なる表現(AS doublefloatおよびintそれぞれ)、および無限(avaliable場合)を有するものを生成することによって戻すことができたlog(0)か何か。C99でも、それらをチェックする標準的な方法はありません。<float.h>ヘッダ(<limits.h>整数型のためのものである)は、約残念ながら静かであるinfnan値。
クリス・ルッツ

うわー、それは大きな混乱です。私のコメントが言うようにではなく、をnanl()返します。入力しているときに気づかなかった理由がわかりません。long doubleint
クリス・ルッツ

@クリス、C99の私の答えを見てください。
Alok Singhal

2
@ IngeHenriksen-MicrosoftがVC ++がC99をサポートするつもりはないと述べたことは確かです。
クリス・ルッツ

24

これはとの両方floatで機能しdoubleます:

double NAN = 0.0/0.0;
double POS_INF = 1.0 /0.0;
double NEG_INF = -1.0/0.0;

編集:誰かがすでに言ったように、古いIEEE規格は、そのような値は罠を引き起こすはずだと言っていました。ただし、トラップはエラー処理に干渉するため、新しいコンパイラはほとんどの場合、トラップをオフにして指定された値を返します。


トラッピングは、754-1985で許可されたエラー処理の1つのオプションでした。最新のハードウェア/コンパイラーで使用されている動作も許可されていました(そして、委員会のメンバーの多くにとって好ましい動作でした)。多くの実装者は、標準で「例外」という用語が不幸にも使用されているため、トラップが必要であると誤って想定していました。これは、改訂された754-2008で大幅に明確になりました。
スティーブンキャノン

こんにちは、スティーブン、その通りですが、標準には次のようにも書かれています。「ユーザーは、ハンドラーを指定することで、5つの例外のいずれかでトラップを要求できるはずです。既存のハンドラーを無効にするように要求できるはずです。 、保存、または復元されました。また、指定された例外の特定のトラップハンドラが有効になっているかどうかを判断できる必要があります。」定義されている「すべき」(2.定義)は「強く推奨」を意味し、アーキテクチャなどによって実用的でない場合にのみ、その実装を除外する必要があります。80x86は標準を完全にサポートしているため、Cが標準をサポートしない理由はありません。
Thorsten S.

Cが754(2008)浮動小数点を必要とすることに同意しますが、そうしないのには十分な理由があります。具体的には、Cは、ハードウェア浮動小数点を持たない組み込みデバイスや、プログラマーが浮動小数点を使用したくない信号処理デバイスなど、x86以外のあらゆる種類の環境で使用されます。正しいか間違っているかにかかわらず、これらの使用は言語仕様の多くの慣性を説明します。
スティーブンキャノン

なぜトップアンサーがそこにたどり着いたのかわかりません。要求された値を生成する方法はありません。この答えはそうです。
ドライダム2014年

#define is_nan(x) ((x) != (x))NANの簡単でポータブルなテストとして役立つ場合があります。
ボブ・スタイン

21

これらを取得するためのコンパイラに依存しない方法ですが、プロセッサに依存しない方法です。

int inf = 0x7F800000;
return *(float*)&inf;

int nan = 0x7F800001;
return *(float*)&nan;

これは、IEEE 754浮動小数点形式(x86が使用)を使用するすべてのプロセッサで機能するはずです。

更新:テストおよび更新されました。


2
@ WaffleMatt-なぜこのポートは32/64ビットの間にないのですか?IEEE 754単精度浮動小数点数は、基盤となるプロセッサのアドレス指定サイズに関係なく32ビットです。
アーロン

6
にキャスト(float &)?それは私にはCのようには見えません。必要なものint i = 0x7F800000; return *(float *)&i;
Chris Lutz

6
0x7f800001これは、IEEE-754標準のいわゆるシグナリングNaNであることに注意してください。ほとんどのライブラリとハードウェアはNaNのシグナリングをサポートしていませんが、のような静かなNaNを返す方がよいでしょう0x7fc00000
スティーブンキャノン

6
警告:これにより、厳密なエイリアシングルールに違反して未定義の動作がトリガーされる可能性があります。型のパンニングを行うための推奨される(そしてコンパイラーで最もよくサポートされる)方法は、共用体メンバーを使用することです。
ulidtko 2015年

2
@ulidtkoが指摘した厳密なエイリアシングの問題に加えて、これは、ターゲットが整数に浮動小数点と同じエンディアンを使用することを前提としていますが、常にそうであるとは限りません。
mr.stobbe

15
double a_nan = strtod("NaN", NULL);
double a_inf = strtod("Inf", NULL);

4
これはスマートなポータブルソリューションです!C99はstrtod、NaNとInfを必要とし、変換します。
ulidtko 2015年

1
このソリューションには欠点があるわけではありません。それらは定数ではありません。これらの値を使用して、たとえばグローバル変数を初期化する(または配列を初期化する)ことはできません。
マーク

1
@マーク。これらを一度呼び出してグローバル名前空間に設定する初期化関数をいつでも持つことができます。これは非常に実行可能な欠点です。
狂牛病の物理学者

3
<inf.h>

/* IEEE positive infinity.  */

#if __GNUC_PREREQ(3,3)
# define INFINITY   (__builtin_inff())
#else
# define INFINITY   HUGE_VALF
#endif

そして

<bits/nan.h>
#ifndef _MATH_H
# error "Never use <bits/nan.h> directly; include <math.h> instead."
#endif


/* IEEE Not A Number.  */

#if __GNUC_PREREQ(3,3)

# define NAN    (__builtin_nanf (""))

#elif defined __GNUC__

# define NAN \
  (__extension__                                  \
   ((union { unsigned __l __attribute__ ((__mode__ (__SI__))); float __d; })  \
    { __l: 0x7fc00000UL }).__d)

#else

# include <endian.h>

# if __BYTE_ORDER == __BIG_ENDIAN
#  define __nan_bytes       { 0x7f, 0xc0, 0, 0 }
# endif
# if __BYTE_ORDER == __LITTLE_ENDIAN
#  define __nan_bytes       { 0, 0, 0xc0, 0x7f }
# endif

static union { unsigned char __c[4]; float __d; } __nan_union
    __attribute_used__ = { __nan_bytes };
# define NAN    (__nan_union.__d)

#endif  /* GCC.  */

0

また、これらがコンパイル時定数ではないことにも驚いています。しかし、そのような無効な結果を返す命令を実行するだけで、これらの値を簡単に作成できると思います。0で割ると、0の対数、90の日焼け、それはちょっとしたことです。


0

普段使っています

#define INFINITY (1e999)

または

const double INFINITY = 1e999

これは、表現可能な最大のdouble値がおおよそであるため、少なくともIEEE754コンテキストで機能し1e308ます。1e309と同じように機能しますが、1e99999スリーナインで十分で記憶に残ります。これはdoubleリテラル(この#define場合)または実際のInf値のいずれかであるため、128ビット(「longdouble」)浮動小数点数を使用している場合でも無限のままになります。


1
私の意見では、これは非常に危険です。誰かがあなたのコードを20年かそこらで128ビットフロートに移行する方法を想像してみてください(あなたのコードが信じられないほど複雑な進化を遂げた後、今日あなたが予測できなかった段階はありません)。突然、指数の範囲が大幅に増加し、すべての1e999リテラルがに丸められなくなります+Infinity。マーフィーの法則によれば、これはアルゴリズムを破ります。さらに悪いことに、「128ビット」ビルドを実行している人間のプログラマーは、そのエラーを事前に発見することはないでしょう。つまり、このエラーが検出されて認識されるのは遅すぎる可能性があります。とても危ない。
ulidtko 2015年

1
もちろん、上記の最悪のシナリオは現実的とはほど遠いかもしれません。しかし、それでも、代替案を検討してください!安全側にとどまる方が良いです。
ulidtko 2015年

2
「20年後」、へぇ。いい加減にして。この回答はないという悪いです。
alecov 2016年

@ulidtko私もこれは好きではありませんが、本当に?
Iharob Al Asimi 2016

0

これらの定数を定義する簡単な方法は次のとおりです。移植性があると確信しています。

const double inf = 1.0/0.0;
const double nan = 0.0/0.0;

このコードを実行すると:

printf("inf  = %f\n", inf);
printf("-inf = %f\n", -inf);
printf("nan  = %f\n", nan);
printf("-nan = %f\n", -nan);

私は得る:

inf  = inf
-inf = -inf
nan  = -nan
-nan = nan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.