ENABLE_NAN_DIAGNOSTICと浮動小数点がNaNになる場合のデバッグ

特定の条件でバウンディングボックスの内容がNaNになるバグの調査中にUE4にNaNチェックの仕組みがあるのを発見したので情報提供します。

UnrealMathUitility.hの中で ENABLE_NAN_DIAGNOSTIC というdefine定数が定義されています。
これを1にすることで、FVectorなどのfloatを扱う変数にNaN値がセットされたときにログを出力することができます。
またこのdefineはDebugビルド時は自動的に有効になります。
ENABLE_NAN_DIAGNOSTIC が有効な状態ではエンジン内部でNaN値を検出した際に logOrEnsureNanError マクロでログが出力されます。

このログマクロですが、デフォルトではログを出力するだけですが、GEnsureOnNANDiagnostic というグローバル変数が1の場合は、ensureMsgfに変わるので、NaN検出時に例外でBreakするようになります。
GEnsureOnNANDiagnostic はコンソールコマンドで変更できるので、実行中に

EnsureOnNaNFail 1

で有効になります。

まとめると、DebugEditorかDebugでビルドして実行し、コンソールで "EnsureOnNaNFail 1"を実行することで、なにかの浮動小数点変数がNaNになったときにBreakすることができます。便利ですね。

ここで一つ注意点。
ENABLE_NAN_DIAGNOSTIC が有効になっていると、エンジン内部で値を初期値に置き換えてしまう処理が随所に入っています。プログラムをクラッシュさせないためだと思うのですが、むしろDebugビルドではクラッシュしてくれた方が良い場合もあります。

例を挙げると

bool USceneComponent::InternalSetWorldLocationAndRotation(FVector NewLocation, const FQuat& RotationQuat, bool bNoPhysics, ETeleportType Teleport)
{
	checkSlow(bComponentToWorldUpdated);
	FQuat NewRotationQuat(RotationQuat);

#if ENABLE_NAN_DIAGNOSTIC
	if (NewRotationQuat.ContainsNaN())
	{
		logOrEnsureNanError(TEXT("USceneComponent:InternalSetWorldLocationAndRotation found NaN in NewRotationQuat: %s"), *NewRotationQuat.ToString());
		NewRotationQuat = FQuat::Identity;
	}
#endif

といった感じになっています。
これを知らずにいると、Developmentで出ているバグがDebugビルドでは発生しないということも起き得るので、注意が必要だと思います。なぜかDebugビルドでは発生しないバグに出会ったら、これを疑ってみると良いのではと思います。

できれば、この値補正機能を切れるdefineも欲しいところです。

いつもありがとうございます!こういった部分はソースを読むエンジニア向けの機能で、なかなかドキュメントとして精査しづらいなか、このように共有していただけるのは非常に助かります!