読者です 読者をやめる 読者になる 読者になる
ようこそ。睡眠不足なプログラマのチラ裏です。

なんぞこれ。NaNってなんなんすか?あのね、非数のことだよ。なんだかナンのカレーが食べたくなるね。ヒ素じゃなくてよかった。

プログラミング C# .NET

ネタ元
びっくりした事 - えムナウBlog (わんくま同盟Blog's)
http://blogs.wankuma.com/mnow/archive/2009/10/14/182100.aspx

以下のコンソールアプリケーションで"NG"と表示するケースがあった。

class Program 
{ 
 static void Main(string[] args) 
 { 
  double d1 = ある計算式; 
  double d2 = d1; 
  Check(d1, d2); 
 } 
 private static void Check(double d1, double d2) 
 { 
  if (d1 == d2) 
  { 
   Console.WriteLine("OK"); 
  } 
  else 
  { 
   Console.WriteLine("NG"); 
  } 
 } 
} 


非常にびっくりしたのでクイズにします。
ある計算式とは何でしょう?
一番簡単(文字数が少なく)に書いてみて!


結論から言うと、このコードで"NG"が出力される「ある計算式」とは、
演算結果が「double.NaN」になるようなものです。
NaN (Not a Number)*1とは結果が存在しない演算の結果(非数)として得られるもので、
たとえば、.0/0ですとか、0/0Dなどが演算結果が得られないので非数ということになります。


えムナウさんが「大文字を別解とすると 28パターン(最短文字数の4文字で書く場合)」とおっしゃっていますが、
それは誤りです。自分も間違えないように覚書としてチラ裏に記録することにしました。
コメント欄にて、「どうでもいい別解」と書かせていただいたのは、パターンが多すぎたためです。


それ本当に28パターンだけですか?

では、NaNなんパターンあるのでしょうか。少し酔っ払っているので、ちょっと自信がありませんが、
この問題のコードでは、127パターンあると思います。以下C#のコードで示します。

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        enum testResult
        {
            OK,
            NG
        }

        static void Main(string[] args)
        {
            int count = 0;
            foreach (var dd in GetTestData())
            {
                double d2 = dd;
                if (Check(dd, d2) == testResult.NG)
                {
                    count++;
                }
            }
            Console.WriteLine(count);
            Console.ReadKey();
        }
        private static testResult Check(double d1, double d2)
        {
            if (d1 == d2)
            {
                Console.WriteLine(testResult.OK);
                return testResult.OK;
            }
            return testResult.NG;
        }

        private static IEnumerable<double> GetTestData()
        {
            //.0
            yield return 0 / .0;
            yield return 0 % .0;
            yield return .0 / 0;
            yield return .0 % 0;

            yield return 1 % .0;
            yield return 2 % .0;
            yield return 3 % .0;
            yield return 4 % .0;
            yield return 5 % .0;
            yield return 6 % .0;
            yield return 7 % .0;
            yield return 8 % .0;
            yield return 9 % .0;

            //d
            yield return 0 / 0d;
            yield return 0 % 0d;
            yield return 0 / 0D;
            yield return 0 % 0D;

            yield return 0d / 0;
            yield return 0d % 0;
            yield return 0D / 0;
            yield return 0D % 0;

            yield return 1 % 0d;
            yield return 2 % 0d;
            yield return 3 % 0d;
            yield return 4 % 0d;
            yield return 5 % 0d;
            yield return 6 % 0d;
            yield return 7 % 0d;
            yield return 8 % 0d;
            yield return 9 % 0d;

            yield return 1 % 0D;
            yield return 2 % 0D;
            yield return 3 % 0D;
            yield return 4 % 0D;
            yield return 5 % 0D;
            yield return 6 % 0D;
            yield return 7 % 0D;
            yield return 8 % 0D;
            yield return 9 % 0D;

            yield return 1d % 0;
            yield return 2d % 0;
            yield return 3d % 0;
            yield return 4d % 0;
            yield return 5d % 0;
            yield return 6d % 0;
            yield return 7d % 0;
            yield return 8d % 0;
            yield return 9d % 0;

            yield return 1D % 0;
            yield return 2D % 0;
            yield return 3D % 0;
            yield return 4D % 0;
            yield return 5D % 0;
            yield return 6D % 0;
            yield return 7D % 0;
            yield return 8D % 0;
            yield return 9D % 0;

            //f
            yield return 0 / 0f;
            yield return 0 % 0f;
            yield return 0 / 0F;
            yield return 0 % 0F;

            yield return 0f / 0;
            yield return 0f % 0;
            yield return 0F / 0;
            yield return 0F % 0;

            yield return 1 % 0f;
            yield return 2 % 0f;
            yield return 3 % 0f;
            yield return 4 % 0f;
            yield return 5 % 0f;
            yield return 6 % 0f;
            yield return 7 % 0f;
            yield return 8 % 0f;
            yield return 9 % 0f;

            yield return 1 % 0F;
            yield return 2 % 0F;
            yield return 3 % 0F;
            yield return 4 % 0F;
            yield return 5 % 0F;
            yield return 6 % 0F;
            yield return 7 % 0F;
            yield return 8 % 0F;
            yield return 9 % 0F;

            yield return 1f % 0;
            yield return 2f % 0;
            yield return 3f % 0;
            yield return 4f % 0;
            yield return 5f % 0;
            yield return 6f % 0;
            yield return 7f % 0;
            yield return 8f % 0;
            yield return 9f % 0;

            yield return 1F % 0;
            yield return 2F % 0;
            yield return 3F % 0;
            yield return 4F % 0;
            yield return 5F % 0;
            yield return 6F % 0;
            yield return 7F % 0;
            yield return 8F % 0;
            yield return 9F % 0;

            //e
            yield return 0 / 0e;
            yield return 0 % 0e;
            yield return 0 / 0E;
            yield return 0 % 0E;

            yield return 0e / 0;
            yield return 0e % 0;
            yield return 0E / 0;
            yield return 0E % 0;

            yield return 1 % 0e;
            yield return 2 % 0e;
            yield return 3 % 0e;
            yield return 4 % 0e;
            yield return 5 % 0e;
            yield return 6 % 0e;
            yield return 7 % 0e;
            yield return 8 % 0e;
            yield return 9 % 0e;

            yield return 1 % 0E;
            yield return 2 % 0E;
            yield return 3 % 0E;
            yield return 4 % 0E;
            yield return 5 % 0E;
            yield return 6 % 0E;
            yield return 7 % 0E;
            yield return 8 % 0E;
            yield return 9 % 0E;

        }
    }
}


実行結果

127

おっ!ナイスフリードマン数!(何

ということで、28パターンではありませんでした。

C#において、NaN(非数)となる除算演算と剰余演算とは

では、C#ではどのような演算を行うとNaN(非数)となるのでしょうか。
はい、そのあたりの詳しい仕様については、MSDNにちゃんと書いてあります。


C# 言語の仕様
7.7.2 除算演算子
http://msdn.microsoft.com/ja-jp/library/aa691373(VS.71).aspx

商は、IEEE 754 の演算規則に従って計算されます。次に示す表は、非ゼロ有限値、
ゼロ、無限大、および NaN の可能なすべての組み合わせの結果をまとめたものです。
x および y は正の有限値で、z は x / y の結果です。
結果を格納する型に対して結果が大きすぎる場合、z は無限大になります。
結果を格納する型に対して結果が小さすぎる場合、z はゼロになります。



7.7.3 剰余演算子
http://msdn.microsoft.com/ja-jp/library/aa691374(VS.71).aspx

次に示す表は、非ゼロ有限値、ゼロ、無限大、および NaN の
可能なすべての組み合わせの結果をまとめたものです。x および y は正の有限値です。
z は x % y の結果で、x - n * y を計算して得られます。
n は、x / y より小さいか等しい中で最大の整数です。
この剰余計算方法は、整数オペランドに対して使用されるものとほぼ同じですが、
IEEE 754 の定義 (n は x / y に最も近い整数) とは異なっています。




ということでした。浮動小数値の演算を扱うような場合は注意したいですね。
あとふざけたタイトルですいません(思いついちゃったんだからしょーがない。)


(追記:2009/10/17 10:12)
ちなみに、指定した数値がNaN (非数)であるかどうかを評価するには、
double.IsNaNメソッドを用います。
http://msdn.microsoft.com/ja-jp/library/system.double.isnan.aspx

*1:NaN値と呼ばれる特殊な浮動小数値群は、IEEE754規格で定められている。NaN値が用いられるケースとしては、本来の演算結果が実数域にも無限大値にも収まらない場合(例えば負数の平方根など)や、演算結果が数学的に正しく表現できない場合などがある