NaN(Not a Number)
NaN(Not a Number)とは非数を表す値のことです。NaNは数ではない値を表現する際に使われます。また例外的な結果や異常値であることを表現する際の値として使われることもあります。
NaNは非数を表すためだけではなく、無効な演算や不正な処理が行われたことを表すための値としても利用されています。
目次
NaNの発生条件
多くのプログラミング言語ではゼロ同士の除算0.0 / 0.0
でNaNが発生します。NaNに対する算術演算の結果もNaNになります(NaNは伝播する)。また無限大を表すInfinity(Inf)の値との演算でNaNが発生することもあります。JavaScript等のスクリプト言語では9 * "a"
や1 + undefined
等の非数となりうる演算処理でもNaNが発生します。
/* ゼロ同士の除算 */
printf("%f", 0.0 / 0.0); // "nan"
/* NaNとの算術演算 */
printf("%f", NAN + 1); // "nan"
/* 数学関数の定義域エラー */
printf("%f", asin(1.1)); // "nan" (区間外の逆三角関数)
printf("%f", sqrt(-0.1)); // "nan" (負数の平方根)
printf("%f", log(-0.1)); // "nan" (負数の対数)
printf("%f", fmod(1, 0)); // "nan" (ゼロ除算を伴う剰余演算)
// javascript: 1 % 0 // NaN
/* Infとの算術演算 */
double inf = 1 / 0.0;
printf("%f", inf * 0); // "nan"
printf("%f", inf / inf); // "nan"
printf("%f", inf - inf); // "nan"
printf("%f", inf + -inf); // "nan"
NaNの性質
NaNは以下の性質を持ちます。
- NaNとの比較は等しくならない
- NaNとの算術演算の結果はNaNになる
/* 等値比較、大小比較 */
NaN == 0; // false
NaN > 0; // false
NaN < 0; // false
NaN == NaN; // false
NaN != NaN; // true
/* 算術演算 */
NaN + 99; // NaN
NaN - NaN; // NaN
qNaN (quiet NaN)
qNaN(quiet NaN)は演算によって伝播するNaNです。qNaNは浮動小数点例外を発生させません。qNaNはゼロ除算のような数学的に定義されていないような演算で発生する事があります。
sNaN (signaling NaN)
sNaN(signaling NaN)は発生時に浮動小数点例外を発生させるNaNです。sNaNは不正な演算を行った際に発生することがあります。たとえば、0による剰余(2 % 0
, fmod(2, 0)
)はゼロ除算により解を定めることができないため、結果はsNaNとなり、同時に不正例外を発生させます。
浮動小数点例外
浮動小数点演算において発生する例外を浮動小数点例外(floating-point exception)と呼びます。浮動小数点例外には主に、ゼロ除算による例外やオーバーフロー、アンダーフローによる例外、不正確な結果の発生(丸め誤差など)、無効な演算を表す例外などが含まれます。
C言語やC++ではfenv.h
ヘッダのfetestexcept
関数を用いて、発生した浮動小数点例外の種類を特定することが可能となっています。
// #include <fenv.h>
printf("%f", fmod(2, 0)); // "nan" (ゼロ除算を伴う剰余演算, 第二引数に無効な値を指定)
if (fetestexcept(FE_INVALID)) {
puts("浮動小数点例外が発生しました");
}
feclearexcept(FE_ALL_EXCEPT); // 現在発生している例外を全てクリア
printf("%f", log(0)); // "-inf"
if (fetestexcept(FE_DIVBYZERO)) {
puts("浮動小数点例外が発生しました");
}
printf("%f", exp(710)); // "inf" (オーバーフロー)
assert( fetestexcept(FE_ALL_EXCEPT) & (FE_INEXACT | FE_OVERFLOW) );
printf("%f\n", 1.0 / 3.0); // "0.333333" (情報落ち)
assert( fetestexcept(FE_ALL_EXCEPT) & FE_INEXACT );
double a = 0.5, b = 0.1; // {a: 0.5, b: 0.10000000000000001}
double c = a - b; // {c: 0.40000000000000002} (丸め誤差)
assert( fetestexcept(FE_ALL_EXCEPT) & FE_INEXACT );
浮動小数点例外の状態を表すフラグは以下の通りです。
定数 | 浮動小数点例外の状態 |
---|---|
FE_DIVBYZERO | ゼロ除算 |
FE_INEXACT | 不正確な結果 |
FE_INVALID | 無効な操作、不正な操作、未定義の操作 |
FE_OVERFLOW | オーバーフローの発生 |
FE_UNDERFLOW | アンダーフローの発生 |
FE_ALL_EXCEPT | 処理系が定義する全ての例外を表す際に指定 |
浮動小数点例外を意図的に発生させる場合にはferaiseexcept
関数を用います。
/* 例外の追加 */
feraiseexcept(FE_DIVBYZERO); // ゼロ除算による例外を起こす
feraiseexcept(FE_INVALID | FE_INEXACT); // 複数の例外を起こす
/* 例外の発生を確認 */
assert( fetestexcept(FE_INEXACT) );
assert( fetestexcept(FE_ALL_EXCEPT) & (FE_DIVBYZERO | FE_INVALID) );
/* 例外の解除 */
feclearexcept(FE_INEXACT); // ゼロ除算のみクリア
feclearexcept(FE_DIVBYZERO | FE_INEXACT); // 複数の例外をクリア
feclearexcept(FE_ALL_EXCEPT); // 全てクリア
NaNの定数定義
プログラミング言語の種類によってはNaN
と言う定数が予め用意されていたり、Float型の名前空間内にNaNが定義されている場合もあります。
var nan = NaN; // JavaScript
nan = Float::NAN // Ruby
float nan = NAN; // C言語
float nan = std::numeric_limits<float>::quiet_NaN(); // C++
NaNの定数定義(C言語)
C言語ではmath.h
ヘッダーにNAN
を表現するマクロが定義されています。マクロ定数NANの値はqNaN(quiet NaN)を表します。
// #include <math.h>
// #define NAN __builtin_nanf("0x7fc00000")
float nan = NAN;
NaNの定数定義(C++)
C++ではlimits
ヘッダーのnumeric_limits
クラスにNaNを取得するための関数が定義されています。
種別 | 関数名 |
---|---|
qNaN (quiet NaN) | std::numeric_limits<double>::quiet_NaN() |
sNaN (signaling NaN) | std::numeric_limits<double>::signaling_NaN() |
quiet_NaN
関数でNaNを取得できます。signaling_NaN
は浮動小数点例外を発生させる特殊なNaN(sNaN)です。
// #include <limits>
// float quiet_NaN() { return __builtin_nanf(""); }
// float signaling_NaN() { return __builtin_nansf(""); }
float q_nanf = std::numeric_limits<float>::quiet_NaN();
float s_nanf = std::numeric_limits<float>::signaling_NaN();
double q_nan = std::numeric_limits<double>::quiet_NaN();
double s_nan = std::numeric_limits<double>::signaling_NaN();
NaNの判定方法
NaNには、NaN同士の比較演算が等しくならないという特殊な性質があるため、非等値演算子によるNaNの判定も可能となっています。
float nan = NAN;
if (nan != nan) puts("YES I AM!");
ただし、基本的にはプログラミング言語側で定義されているisnan
関数やisNaN
関数を使うようにしましょう。
/* C言語 */
isnan(NAN); // 1
/* C++ */
std::isnan(NAN); // true
/* JavaScript */
Number.isNaN(NaN) // true
/* Ruby */
nan = Float::NAN
nan.nan? // true
NaNの判定方法(C言語)
C言語ではmath.h
ヘッダーにNaN判定用のisnan
マクロが定義されています。
// #include <math.h>
float nan = NAN;
printf("%d", isnan(nan)); // 1
NaNの判定方法(C++)
C++言語の場合はcmath
ヘッダーのstd::isnan()
関数を利用することができます。
// #include <cmath>
float q_nan = std::numeric_limits<float>::quiet_NaN();
float s_nan = std::numeric_limits<float>::signaling_NaN();
if (std::isnan(q_nan) && std::isnan(s_nan))
puts("Yes we NaN!");
NaNの判定方法(JavaScript)
JavaScriptの場合はisNaN
関数が用意されていますが、厳格な比較が行われないという問題があるため、実際にはNaN !== NaN
のイディオムがよく使われています。なおECMAScript 6(2015)ではより堅牢なNumber.isNaN()
関数が用意されています。
// 型強制後の値で判定されてしまう
isNaN(NaN) // true
isNaN({}) // true
isNaN("a") // true
isNaN(undefined) // true
isNaN(false) // false
isNaN(null) // false
isNaN([]) // false
isNaN(0) // false
// NaN判定イディオム
var nan = NaN
nan !== nan // true
nan === nan // false
var nan = "a";
typeof nan === "number" && isNaN(nan) // false
// ECMAScript 6
Number.isNaN(NaN) // true
Number.isNaN({}) // false
Number.isNaN("a") // false
NaNの整数変換
NaNの値をint型やlong型の整数に変換すると、NaNの情報が失われ、処理系依存の値に変化するため注意が必要です。Clangコンパイラでは各整数型の最小値に変換されることが確認できますが、他の処理系では異なる結果となる場合があります。
float nan = NAN; // nan == NaN
int i = nan; // i == -2147483648
nan = i; // nan == -2147483648.0
float nan = NAN;
printf("%d", (int)nan); // "-2147483648" (INT_MIN == -2147483648)
printf("%ld", (long)nan); // "-9223372036854775808" (LONG_MIN)
NaNの評価
NaNに対する条件式の評価は真になることがほとんどですが、JavaScriptでは偽になるため注意が必要です。
/* C/C++ */
if (NAN) puts("yes"); // "yes"
/* Ruby */
Float::NAN ? "yes" : "no" // "yes"
/* JavaScript */
NaN ? "yes" : "no"; // "no"
~~NaN ? "yes" : "no"; // "yes"
NaNの内部表現
IEEE 754におけるNaNのデータ表現は、浮動小数点数型の指数部のビットを全て1、仮数部の値を0以外にする形で表されています。
型 | 値 | 内部表現(ビット) |
---|---|---|
qNaN【0.0 / 0.0, NAN, quiet_NaN()】 | ||
float | 0x7fc00000 | 01111111110000000000000000000000 |
double | 0x7ff8000000000000 | 0111111111111000000000000000000000000000000000000000000000000000 |
sNaN【fmod(2, 0)】 | ||
float | 0xffc00000 | 11111111110000000000000000000000 |
double | 0xfff8000000000000 | 1111111111111000000000000000000000000000000000000000000000000000 |
sNaN【signaling_NaN()】 | ||
float | 0x7fa00000 | 01111111101000000000000000000000 |
double | 0x7ff4000000000000 | 0111111111110100000000000000000000000000000000000000000000000000 |
浮動小数点数型 | フォーマット |
---|---|
半精度浮動小数点数(float) | (符号部 1bit)+(指数部 5bit)+(仮数部 10bit) |
単精度浮動小数点数(double) | (符号部 1bit)+(指数部 8bit)+(仮数部 23bit) |
// #define NAN __builtin_nanf("0x7fc00000")
// float quiet_NaN() { return __builtin_nanf(""); }
// float signaling_NaN() { return __builtin_nansf(""); }
double q_nan = std::numeric_limits<double>::quiet_NaN();
double s_nan = std::numeric_limits<double>::signaling_NaN();
double x_nan = 0.0 / 0.0;
double y_nan = fmod(2, 0);
double z_nan = NAN;
if (q_nan && s_nan && x_nan && y_nan && z_nan)
printf("%f, %F", x_nan + y_nan, z_nan - z_nan); // "nan, NAN"
if (std::isnan(q_nan) && std::isnan(s_nan) &&
std::isnan(x_nan) && std::isnan(y_nan) && std::isnan(z_nan))
puts("Yes we NaN!"); // "Yes we NaN!"
整数への変換実験
NaNの整数変換については「# NaNの整数変換」を参考。
/* 処理系依存の型変換 in LLVM/Clang */
int inan = std::numeric_limits<double>::quiet_NaN(); // (cvttsd2si)
inan; // -2147483648 (INT_MIN)
long lnan = std::numeric_limits<double>::quiet_NaN(); // (cvttsd2si)
lnan; // -9223372036854775808 (LONG_MIN)
int inanf = std::numeric_limits<float>::quiet_NaN(); // (cvttss2si)
inanf; // -2147483648 (INT_MIN)
NaNの演算実験
NaNの性質については「# NaNの性質」を参考。
double nan = NAN;
/* trueになる演算 */
if (nan != nan && nan - nan && nan + 999 &&
nan != 999 && nan != 0x7ff8000000000000
) {
puts("y"); // 表示される
}
/* falseになる演算 */
if (nan == nan || !nan ||
nan == 0x7ff8000000000000 ||
nan <= nan || nan > nan ||
nan >= nan || nan < nan ||
nan <= 0 || nan >= 0
) {
puts("n"); // 表示されない
}