型を変えただけなのに §
サンプルソースのdiv1のようにdecimal型の割り算を行うメソッドがあるとします。
0を0で割ると【ゼロで割った】という例外が起きます。
ところが、【decimal型をやめてdouble型に変更してよ】と言われてサンプルソースのdiv2
のように書き換えると他には何も変更していないのに例外が起きなくなり、その代わりにNaNという数値ではない謎の文字が出力されるようになりました。
なぜでしょう?
NaNは数値ではない数値 §
NaNとは、非数(Not a Number)を示す浮動小数点数値の状態です。
つまり、数値の一種であって、数値ではありません。
C#の数値型には、このような状態を表現する能力を持っている型と持っていない型があります。持っているのはfloat/doubleです。持っていないのはそれ以外の全てです。
0を0で割った結果の値は数値としては表現できないので、NaNという状態になります。
ちなみに、0意外の数を0で割ると∞等の状態になります。これも、数値の一種でありながら数値ではない状態です。
NaNを表現する機能を持っていない型の場合は状態がNaNになることはできないので、例外が起きます。つまり、型を書き換えるだけで動作が変わります。
罠の数々 §
- 表現力の乏しい型の方が、より省メモリで速い可能性が高い。そのため、表現力の乏しい型も積極的にサポートされている。必要なニーズを満たす最小の型を調べてそれを使うとそれだけで性能アップが期待できる
- 表現力の高い型は、NaNや∞のような数値であって数値ではない状態に簡単に入るので、必ず何かの数値になると期待してはいけない。
- DivideByZeroExceptionは0で割った時に必ず起こるわけではない。それは型に依存する
参考リンク §
算術演算子 (C# リファレンス)
DivideByZeroException クラス
どの型で割り算した時に例外になるかの条件は、実は算術演算子の説明ページではなく、DivideByZeroException クラスのページに書いてある。チェックしてみよう。
リポジトリ §
https://github.com/autumn009/cshowto
DivByZero §
using System;
class Program
{
private static void div1(decimal x, decimal y)
{
Console.WriteLine(x / y);
}
private static void div2(double x, double y)
{
Console.WriteLine(x / y);
}
static void Main()
{
Console.WriteLine("div1の利用");
try
{
div1(0, 0);
}
catch (DivideByZeroException)
{
Console.WriteLine("0で割りました。");
}
Console.WriteLine("div2の利用");
try
{
div2(0, 0);
}
catch (DivideByZeroException)
{
Console.WriteLine("0で割りました。");
}
}
}
実行結果
div1の利用
0で割りました。
div2の利用
NaN