C#の浮動小数点のオーバーフロー判定に注意:checked抜ける

C#のイメージ。

Untiy を使った開発を C# で行っていたのですが、オーバーフローのチェックで躓きました。 C# では、浮動小数点のオーバーフローの扱いに注意する必要があるようです。

一般に、コンパイラオプションないし checked, unchecked 構文によってオーバーフローの検出は可能ですが、 浮動小数点の場合には、checked によるオーバーフローの検出は有効ではありませんでした。 (なお他の方法については検証していません。)

検証コード

以下のコードのように int と float で意図的にオーバーフローを起こして検証しました。 (double についても float と同じ結果が得られていますが割愛)

  • 検証環境
    • Windows10 Pro 64bit
    • VisualStudio Professional 2015
    • .NET Framework 4.6.x
int intValue = int.MaxValue;
float floatValue = float.MaxValue;

#region int

try
{
	checked
	{
		intValue += 1;
	}
}
catch (System.OverflowException e)
{
	Console.WriteLine("int Exception(A) : " + e);
}
catch (System.Exception e)
{
	Console.WriteLine("int Exception(B) : " + e);
}

Console.WriteLine("int MaxValue : " + int.MaxValue);
Console.WriteLine("int currentValue : " + intValue);

#endregion

#region float

try
{
	checked
	{
		floatValue += 1;
	}
}
catch (System.OverflowException e)
{
	Console.WriteLine("float exception(A) : " + e);
}
catch (System.Exception e)
{
	Console.WriteLine("float exception(B) : " + e);
}

Console.WriteLine("float MaxValue : " + float.MaxValue);
Console.WriteLine("float CurrentValue : " + floatValue);

if (float.IsInfinity(floatValue))
{
	Console.WriteLine("floatValue is Infinity");
}
if (float.IsNaN(floatValue))
{
	Console.WriteLine("floatValue is NaN");
}
if (floatValue == float.MaxValue)
{
	Console.WriteLine("floatValue is MaxValue");
}
else
{
	Console.WriteLine("Any");
}

#endregion

結果

結果は次のようになりました。int のオーバーフローは検出されますが、float のオーバーフローは検出されません。 また float 型の値は、NaN でも Infinity でもなく MaxValue を維持していました。

int Exception(A) : System.OverflowException: 算術演算の結果オーバーフローが発生しました…
int MaxValue : 2147483647
int currentValue : 2147483647
float MaxValue : 3.402823E+38
float CurrentValue : 3.402823E+38
floatValue is MaxValue

なお Unity では Mono (実験時 3.x) を使っていますが、同様の現象が確認できています。 先と重なりますが double でも同じ現象が確認できています。

少なくとも checked 構文を使って浮動小数点のオーバーフローを検出できないことは覚えておいたほうがよさそうです。 今後のバージョンアップでは対応される可能性はありますけども。