nanって何なん

メモです

チェック環境 : Visual Studio 2015 Update 3
Releaseビルド



infからnanをつくる

const float nan = numeric_limits<float>::quiet_NaN();
const float inf = numeric_limits<float>::infinity();
printf("nan = %f\n", nan);
printf("inf + inf = %f\n", inf + inf);
printf("inf - inf = %f\n", inf - inf);
printf("inf * inf = %f\n", inf * inf);
printf("inf / inf = %f\n", inf / inf);

[output]
nan = nan
inf + inf = inf
inf - inf = -nan(ind)
inf * inf = inf
inf / inf = -nan(ind)

inf-infがnanっていうの、言われてみれば確かにって感じだった



nanをチェックする

printf("isNan(nan) = %s\n", isnan<float>(nan) ? "true" : "false");
printf("isNan(inf-inf) = %s\n", isnan<float>(inf-inf) ? "true" : "false");

[output]
isNan(nan) = true
isNan(inf-inf) = true

「nan」と「-nan(ind)」という表記ゆれがあったが、isnan()ではどっちもnan判定してくれるらしい



オーバーフローからinfをつくる

const float max = numeric_limits<float>::max();
printf("max = %f\n", max);
for(int i=0; i<100; i++) {
	float x = max + pow(10.f, i);
	if( isinf(x) ) {
		printf("max + 10^%d -> inf\n", i);
		break;
	}
}

[output]
max = 340282346638528859811704183484516925440.000000
max + 10^32 -> inf

オーバーフローによるinf化、maxに足して起こるのは10^32ぐらいが目安



ヒープ汚れによる初期値nan

class A {
public:
	explicit A() {}
public:
	float a, b, c;
};

{
	A* class_a = new A();
	class_a->a = class_a->b = class_a->c = nan;
	printf("1: addr=%p  a=%f  b=%f  c=%f\n", class_a, class_a->a, class_a->b, class_a->c);
	delete class_a;
}
{
	A* class_a = new A();
	// メンバ初期化しない
	printf("2: addr=%p  a=%f  b=%f  c=%f\n", class_a, class_a->a, class_a->b, class_a->c);
	delete class_a;
}

[output]
// だいたいこんな感じになる
1: addr=00919F28  a=nan  b=nan  c=nan
2: addr=00919E68  a=0.000000  b=0.000000  c=0.000000

// だが、15回に1回ぐらいの頻度で同じアドレスにメモリを確保し、初期値がnanになる
1: addr=00DEA200  a=nan  b=nan  c=nan
2: addr=00DEA200  a=nan  b=nan  c=nan

絶対に変数初期化しような


平方根、指数、対数

const float min = -max;
const float eps = numeric_limits<float>::epsilon();

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

printf("exp(max) = %f\n", exp(max));
printf("exp(inf) = %f\n", exp(inf));

printf("log(0)    = %f\n", log(0.f));
printf("log(-eps) = %f\n", log(-eps));
printf("log(inf)  = %f\n", log(inf));

[output]
sqrt(-eps) = -nan(ind)  // 少しでも負ならnan化
sqrt(inf)  = inf

exp(max) = inf  // inf化
exp(inf) = inf  // めちゃくちゃ大きくなるが、数値ではあるのでnanではない

log(0)    = -inf       // 0なら-inf
log(-eps) = -nan(ind)  // 少しでも負なら未定義 -> nan
log(inf)  = inf        // logは収束しそうな形してるけど収束しない

ちなみにnan突っ込んだときは全部nanになる (伝播する)