JavaにおけるNaN比較

先日の続き。
Javaではどうなるか気になったので試してみました。

C:\Develop>copy con NaN.java
public class NaN {
  public static void main (String[] args) {
    System.out.println(0.0 / 0 == 0.0 / 0);
  }
}
^Z
        1 個のファイルをコピーしました。

C:\Develop>^Z

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\javac.exe NaN.java

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\java.exe NaN
false

普通にfalseになるようです。
実際はどんな動きをしているか、javapを使ってみてみました。

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\javap.exe -verbose NaN
(中略)
public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   iconst_0
   4:   invokevirtual   #3; //Method java/io/PrintStream.println:(Z)V
   7:   return
  LineNumberTable:
   line 3: 0
   line 4: 7

…あ、コンパイラが最適化してたorz


というわけで、やりなおし。

C:\Develop>del NaN.*

C:\Develop>copy con NaN.java
public class NaN {
  public static float getZero() {
    return 0.0f;
  }
  public static void main(String[] args) {
    System.out.println(getZero() / 0 == getZero() / 0);
  }
}
^Z
        1 個のファイルをコピーしました。

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\javac.exe NaN.java

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\javap.exe -verbose NaN
(中略)
public static void main(java.lang.String[]);
  Code:
   Stack=4, Locals=1, Args_size=1
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   invokestatic    #3; //Method getZero:()F
   6:   fconst_0
   7:   fdiv
   8:   invokestatic    #3; //Method getZero:()F
   11:  fconst_0
   12:  fdiv
   13:  fcmpl
   14:  ifne    21
   17:  iconst_1
   18:  goto    22
   21:  iconst_0
   22:  invokevirtual   #4; //Method java/io/PrintStream.println:(Z)V
   25:  return
(中略)

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\java.exe NaN
false

今度は問題無さそうです。そして動作は、やはりfalseでした。
# しかし、実行時最適化されているかどうかは不明です
# このあたりのJavaの動きは、ある種クレイジーで面白いわけですが。


では、計算しない場合はどうなのでしょう。

C:\Develop>copy con StaticNaN.java
public class StaticNaN {
  public static float getNaN() {
    return Float.NaN;
  }
  public static void main(String[] args) {
    System.out.println(getNaN() == getNaN());
  }
}
^Z
        1 個のファイルをコピーしました。

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\javac.exe StaticNaN.java

C:\Develop>\Develop\Language\jdk1.6.0_07\bin\javap.exe -verbose StaticNaN
(中略)
public static void main(java.lang.String[]);
  Code:
   Stack=3, Locals=1, Args_size=1
   0:   getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   invokestatic    #4; //Method getNaN:()F
   6:   invokestatic    #4; //Method getNaN:()F
   9:   fcmpl
   10:  ifne    17
   13:  iconst_1
   14:  goto    18
   17:  iconst_0
   18:  invokevirtual   #5; //Method java/io/PrintStream.println:(Z)V
   21:  return
(中略)
C:\Develop>\Develop\Language\jdk1.6.0_07\bin\java.exe StaticNaN
false

やはりfalseです。

こちらもPythonと同じ結果が得られました。

実装上、たまたまそうなっているだけでしょうか。
Java言語仕様を見ると、


a numeric comparison operation involving one or two NaNs returns false and any != comparison involving NaN returns true, including x!=x when x is NaN
と、同一の変数であっても==での比較結果は常にfalseと明示的に書かれています。
世の中的には、どうやらx=NaNの時、x==xはfalseのようです。


しかし、それと数学とが一致しているかは、キット別の話なのかなぁと思いつつ、数学が弱いので判別つきません。
ん〜。数学の勉強、どっかでしなければ。