数値計算の途中で想定外の値が出ると、プログラムは一気に不安定になります。
そこで役立つのが、無限大(infinity)やNaN(非数)を検出するisinfとisnanです。
C言語初心者でも安全に判定できるよう、仕組みや戻り値、実用的なサンプルコードまで丁寧に解説します。
また、正負の無限大の見分け方や注意点も併せて学びます。
isinfとisnanとは
無限大(infinity)とNaN(非数)の基礎
浮動小数点演算では、桁あふれや不正な演算の結果として無限大(+∞や−∞)やNaN(Not a Number)が生じることがあります。
たとえば非常に大きな数の演算で範囲を超えた場合は無限大、0除算や未定義の数学操作(例: 0/0、√−1)ではNaNが発生します。
NaNはどの値とも等しくない(自分自身とも等しくない)という性質を持ちます。
浮動小数点の振る舞いは多くの環境でIEEE 754に準拠します。
この規格に基づき、Cの標準ライブラリは無限大やNaNを扱うための定数や判定マクロを提供しています。
判定に使う標準ライブラリ
<math.h>にはisinfとisnanが用意されており、引数が無限大かNaNかをそれぞれ判定できます。
これらはC99以降では型に依らず(float/double/long double)使えるマクロとして実装されています。
基本はmath.hをインクルードして、条件分岐で利用します。
加えて、INFINITYやNANといった定数マクロ、isfiniteやisnormalなどの関連マクロも同じヘッダにあります。
戻り値と使いどころ
isinf(x)やisnan(x)は条件が真なら非0、偽なら0を返します。
戻り値の具体的な数値(1や−1など)には依存せず、!= 0で扱うのが安全です。
演算結果や外部入力に対して「異常値フィルタ」を掛ける用途で特に有用です。
ログ出力や再計算、デフォルト値への置換などの分岐に活用できます。
以下に主な関数と判定対象の概要を表にまとめます。
| 関数(マクロ) | 真になる条件 | 備考 |
|---|---|---|
| isinf(x) | xが+∞または−∞ | 符号はsignbit(x)で判別 |
| isnan(x) | xがNaN | NaNは比較演算に向きません |
| isfinite(x) | xが有限値 | 無限大とNaNを除外 |
| isnormal(x) | xが正規化数 | 0と非正規化数、無限大、NaNは偽 |
無限大かNaNかの一次判定にisinfとisnan、さらに「普通の数か」まで含めた品質チェックにisfinite/isnormalを足す、という使い分けが基本です。
isinfとisnanの基本の使い方
includeとリンク方法
使うヘッダは#include <math.h>です。
多くのUNIX系環境(GCC/Clang)では、数学ライブラリの関数を使うときリンク時に-lmが必要になります。
判定マクロ自体は関数呼び出しを伴わないこともありますが、慣習的に-lmを付けておくと安心です。
- GCC/Clangの例:
gcc main.c -std=c11 -Wall -Wextra -lm - MSVCの例:
cl /W4 /std:c11 main.c(Windowsでは通常-lmは不要)
if文での判定パターン
基本的な流れは、isnanを最初に確認し、次にisinf、最後に通常の値の処理にフォールバックします。
NaNは他の判定を不安定にすることがあるため、最初に弾くのが実務的です。
- 例:
if (isnan(x)) { /* NaNの処理 / } else if (isinf(x)) { / 無限大の処理 / } else { / 有限の通常処理 */ }
正負の無限大を分けたいときはsignbit(x)を併用します。
戻り値を1や−1に期待せずsignbitで符号を取るのが移植性の高い書き方です。
float/double/long doubleの扱い
C99以降のisinf/isnanは型に依存しないマクロなので、floatでもdoubleでもlong doubleでも同じように呼べます。
printfで出力する際は書式指定子に注意し、doubleは%f、long doubleは%Lfを使います。
リテラルは1.0f(float)、1.0(double)、1.0L(long double)のように後置のサフィックスで型を表します。
サンプルコードの流れ
以下のサンプルでは、さまざまな値に対してisnan/isinf/isfinite/isnormal/signbitを確認し、結果を見やすく表示します。
無限大やNaNの生成には安全な定数INFINITYとNANを用います。
// 判定: isinf, isnan の使い方デモ
// コンパイル例:
// gcc main.c -std=c11 -Wall -Wextra -lm
#include <stdio.h>
#include <math.h> // isinf, isnan, isfinite, isnormal, INFINITY, NAN, signbit
#include <float.h> // 参考用の定数(今回は未使用でも可)
// 真偽を "true"/"false" で表示するユーティリティ
static const char* tf(int cond) { return cond ? "true" : "false"; }
// 値の分類結果を表示
static void print_fp_info(const char* name, double x) {
// 値の表示は参考程度。NaNやinfは実装により "nan", "inf" 等で出ます。
printf("[%s] value=%g\n", name, x);
printf(" isnan : %s\n", tf(isnan(x)));
printf(" isinf : %s\n", tf(isinf(x)));
printf(" isfinite : %s\n", tf(isfinite(x)));
printf(" isnormal : %s\n", tf(isnormal(x)));
// 符号は signbit で取得。負のゼロ(-0.0)でも真になります。
printf(" signbit : %s\n", tf(signbit(x)));
// 無限大の符号を報告(参考)
if (isinf(x)) {
printf(" -> infinity sign: %s\n", signbit(x) ? "negative (-inf)" : "positive (+inf)");
}
puts("");
}
int main(void) {
double a = 1.5; // 通常の有限値
double b = INFINITY; // 正の無限大
double c = -INFINITY; // 負の無限大
double d = NAN; // NaN(非数)
double e = 0.0; // ゼロ(有限、正規/非正規かは値次第)
double f = -0.0; // 負のゼロ(符号ありゼロ)
print_fp_info("finite", a);
print_fp_info("+inf ", b);
print_fp_info("-inf ", c);
print_fp_info("NaN ", d);
print_fp_info("+0.0 ", e);
print_fp_info("-0.0 ", f);
return 0;
}
出力例(環境により表記が変わることがあります)。
[finite] value=1.5
isnan : false
isinf : false
isfinite : true
isnormal : true
signbit : false
[+inf ] value=inf
isnan : false
isinf : true
isfinite : false
isnormal : false
signbit : false
-> infinity sign: positive (+inf)
[-inf ] value=-inf
isnan : false
isinf : true
isfinite : false
isnormal : false
signbit : true
-> infinity sign: negative (-inf)
[NaN ] value=nan
isnan : true
isinf : false
isfinite : false
isnormal : false
signbit : false
[+0.0 ] value=0
isnan : false
isinf : false
isfinite : true
isnormal : false
signbit : false
[-0.0 ] value=-0
isnan : false
isinf : false
isfinite : true
isnormal : false
signbit : true
isnanはNaNだけを検出し、isinfは符号と無関係に無限大を検出するため、signbitと組み合わせれば+∞/−∞まで区別できます。
無限大とNaNを用意する方法
無限大を作る
最も安全で移植性が高いのはINFINITY定数(および+/-での符号付け)を使う方法です。
演算で無理に作らずINFINITYを直接使うのが初心者にはおすすめです。
用途に応じて以下の定数も使えます。
INFINITY: 正の無限大のfloat定数HUGE_VAL,HUGE_VALF,HUGE_VALL: 無限大相当の定数(主にエラー戻り値で使われます)
マイナスを付ければ-INFINITYで負の無限大になります。
NaNを作る
NAN定数を使うのが簡単で安全です。
計算式で0/0を作るなどの手段は避け、定数NANやnan("")系の関数を使うのがよいでしょう。
C99以降ではNaNのペイロードを指定できるnan/nanf/nanlも用意されています。
#include <math.h>
float nf = NAN; // floatのNaN
double nd = NAN; // doubleのNaN(プロモーションされても可)
long double nl = NAN; // long doubleへ代入も可
// ペイロード付きNaN(空文字で汎用的にNaNを得る)
double nd2 = nan("");
NaNは演算に混ざると結果をNaNに伝播させることが多いため、早めの検出とハンドリングが重要です。
正の無限大と負の無限大の区別
isinf(x)は符号を返しません。
符号判定にはsignbit(x)を使います。
#include <math.h>
#include <stdio.h>
int main(void) {
double x = -INFINITY;
if (isinf(x)) {
// signbit(x) は x が負なら true
printf("x is %s infinity\n", signbit(x) ? "negative" : "positive");
}
return 0;
}
x is negative infinity
正負の無限大を明示的に生成したい場合はINFINITYに符号を付けるか、copysign(INFINITY, -1.0)のように符号だけを移す関数を使えます。
初心者が知っておきたい注意点
NaNは==で比較しない
NaNはどの値とも等しくありません。
したがってx == xは通常は真ですがNaNでは偽になります。
NaN判定にはisnan(x)を必ず使い、比較演算で代用しないでください。
#include <stdio.h>
#include <math.h>
int main(void) {
double x = NAN;
printf("x == x ? %s\n", (x == x) ? "true" : "false"); // false になる
printf("isnan(x) ? %s\n", isnan(x) ? "true" : "false"); // true になる
return 0;
}
x == x ? false
isnan(x) ? true
0除算で作らない
浮動小数点の0除算は多くの実装で+∞やNaNを作りますが、実行時に浮動小数点例外を発生させたり、最適化やプラットフォーム差で挙動が変わる可能性があります。
教材目的以外で0除算による生成は避け、INFINITY/NANの定数を使うのが安全です。
関連関数(isfinite, isnormal)の簡単な違い
isfinite(x)は有限であることを確認し、無限大とNaNを除外します。
isnormal(x)は正規化数(十分な桁を持つ通常の浮動小数点数)かどうかを判定し、0や非正規化数、無限大、NaNを偽にします。
「計算を続行してよい通常の値か」を厳しめに見たいときはisnormal、緩めに「有限ならOK」ならisfiniteという使い分けです。
| 判定対象 | isfinite | isnormal | 備考 |
|---|---|---|---|
| 通常の有限値 | 真 | 真 | 例: 1.0, 1.5 |
| 非正規化数 | 真 | 偽 | 非正規はごく小さい数(環境依存) |
| +0.0, −0.0 | 真 | 偽 | 0は正規ではない |
| +∞, −∞ | 偽 | 偽 | 無限大 |
| NaN | 偽 | 偽 | 非数 |
入力の妥当性チェックにはisfinite、数値的に信頼できる範囲に絞るならisnormalという段階的なチェックが実務で便利です。
まとめ
isinfとisnanは、浮動小数点計算の健全性を守るための第一関門です。
math.hをインクルードし、isnanでNaNを最初に排除、isinfで無限大を検知し、必要に応じてsignbitで符号を判断すると、安全で移植性の高いコードになります。
無限大やNaNを作る際はINFINITYやNANなどの定数を使い、0除算で無理に生成しない方針が肝要です。
さらにisfiniteやisnormalを組み合わせれば、入力検証やエラー処理の精度を高められます。
初心者の段階から「異常値をまず疑い、正しく判定する」習慣を身につけることで、後の不具合を大幅に減らせます。
