java.lang.Double

Java の double の非数や無限大の振る舞いについてのメモ。

比較

Groovy のバグ報告で Doublle.NaN == Double.NaN は false と評価されるべきと上がっていた。


Java 言語仕様ではそうなっているらしいので確認してみる。


0 に関する記述もあるのでついでに確認する。

public class DoubleTest extends junit.framework.TestCase {
  public void testNaN() {
    // primitive で比較する場合 NaN は何と比較しても false になる
    assertEquals(false, Double.NaN == Double.NaN);
    assertEquals(false, Double.NaN > Double.MAX_VALUE);
    assertEquals(false, Double.NaN < Double.MAX_VALUE);

    // java.lang.Math で比較する場合 どちらかが NaN であれば NaN を返す
    assertEquals(true,  Double.compare(Double.NaN, Math.max(Double.NaN, Double.MAX_VALUE)) == 0);
    assertEquals(true,  Double.compare(Double.NaN, Math.min(Double.NaN, Double.MAX_VALUE)) == 0);
    assertEquals(true,  Double.compare(Double.NaN, Math.max(Double.NaN, Double.NaN)) == 0);
    assertEquals(true,  Double.compare(Double.NaN, Math.min(Double.NaN, Double.NaN)) == 0);

    // wrapper で比較する場合 NaN は一番大きな数として扱われる
    assertEquals(true,  new Double(Double.NaN).equals(new Double(Double.NaN)));
    assertEquals(true,  new Double(Double.NaN).compareTo(new Double(Double.NaN)) == 0);
    assertEquals(true,  new Double(Double.NaN).compareTo(new Double(Double.MAX_VALUE)) > 0);
    assertEquals(false, new Double(Double.NaN).compareTo(new Double(Double.MAX_VALUE)) < 0);
  }

  public void testZero() {
    // primitive で比較する場合 0.0d と -0.0d は等しい
    assertEquals(true,  0.0d == -0.0d);
    assertEquals(true,  0.0d >= -0.0d);
    assertEquals(true,  0.0d <= -0.0d);
    assertEquals(false, 0.0d >  -0.0d);
    assertEquals(false, 0.0d <  -0.0d);

    // java.lang.Math で比較する場合 0.0d > -0.0d
    assertEquals(true,  Math.max(0.0d, -0.0d) == 0.0d);
    assertEquals(true,  Math.min(0.0d, -0.0d) == -0.0d);

    // wrapper で比較する場合 0.0d > -0.0d である
    assertEquals(true,  new Double(0.0d).compareTo(new Double(-0.0d)) > 0);
    assertEquals(false, new Double(0.0d).compareTo(new Double(-0.0d)) < 0);
  }

  public static void main(String[] args) {
    junit.textui.TestRunner.run(DoubleTest.class);
  }
}

Groovy は double を wrapper で扱っていて、そのまま Java を呼び出しているだけなので特におかしくはない気がする。
この辺りは double が最適化されたら振る舞いが変わってしまう。
コメントにあるように Groovy ではさらに null とも比較できて null は最小の値として扱われる。

groovy:000> null < -Double.MAX_VALUE
===> true
java.lang.Math.pow

Double に目がいったのは高校数学の極限の問題*1を assert でやっていて思った振る舞いと違っていたため。


Groovy の ** 演算子Java の Math.pow を呼び出しているだけ。
以前調べたように、戻り値が int や long の場合は、double からキャストされる。
Java の Math.pow は StrictMath.pow を呼び出している。
StrictMath.pow は native メソッドで fdlibm というライブラリを呼び出しているらしい。
検索してみたらルールが載っていたので確認してみる。

import static java.lang.Double.*

integer      = { Math.random() * Integer.MAX_VALUE as int }
odd_integer  = { def i = integer(); i % 2 == 1 ? i : call() }
even_integer = { def i = integer(); i % 2 == 0 ? i : call() }
non_integer  = { def d = Math.random() * Integer.MAX_VALUE; d != d.round() ? d : call() }

// +-INF
assert POSITIVE_INFINITY == -NEGATIVE_INFINITY
assert NEGATIVE_INFINITY == -POSITIVE_INFINITY

// java.lang.StrictMath
// Java の数学ライブラリは fdlibm のバージョン 5.3 を基に定義されています
// Freely Distributable Math Library (fdlibm)

// StrictMath.pow

// 1.  (anything) ** 0  is 1
assert 0d                ** 0d == 1d
assert POSITIVE_INFINITY ** 0d == 1d
assert NEGATIVE_INFINITY ** 0d == 1d
assert NaN               ** 0d == 1d

// 2.  (anything) ** 1  is itself
assert 0d                ** 1d == 0d
assert POSITIVE_INFINITY ** 1d == POSITIVE_INFINITY
assert NEGATIVE_INFINITY ** 1d == NEGATIVE_INFINITY
assert NaN               ** 1d == NaN

// 3.  (anything) ** NAN is NAN
assert 0d                ** NaN == NaN
assert POSITIVE_INFINITY ** NaN == NaN
assert NEGATIVE_INFINITY ** NaN == NaN
assert NaN               ** NaN == NaN

// 4.  NAN ** (anything except 0) is NAN
assert NaN ** POSITIVE_INFINITY == NaN
assert NaN ** NEGATIVE_INFINITY == NaN
assert NaN ** NaN               == NaN

// 5.  +-(|x| > 1) **  +INF is +INF
assert (+MAX_VALUE) ** POSITIVE_INFINITY == POSITIVE_INFINITY
assert (-MAX_VALUE) ** POSITIVE_INFINITY == POSITIVE_INFINITY

// 6.  +-(|x| > 1) **  -INF is +0
assert (+MAX_VALUE) ** NEGATIVE_INFINITY == +0d
assert (-MAX_VALUE) ** NEGATIVE_INFINITY == +0d

// 7.  +-(|x| < 1) **  +INF is +0
assert (+Math.random()) ** POSITIVE_INFINITY == +0d
assert (-Math.random()) ** POSITIVE_INFINITY == +0d
assert (+0d)            ** POSITIVE_INFINITY == +0d
assert (-0d)            ** POSITIVE_INFINITY == +0d

// 8.  +-(|x| < 1) **  -INF is +INF
assert (+Math.random()) ** NEGATIVE_INFINITY == POSITIVE_INFINITY
assert (-Math.random()) ** NEGATIVE_INFINITY == POSITIVE_INFINITY

// 9.  +-1         ** +-INF is NAN
assert (+1d) ** POSITIVE_INFINITY == NaN
assert (-1d) ** POSITIVE_INFINITY == NaN
assert (+1d) ** NEGATIVE_INFINITY == NaN
assert (-1d) ** NEGATIVE_INFINITY == NaN

// 10. +0 ** (+anything except 0, NAN)               is +0
assert (+0d) ** MIN_VALUE == +0d
assert (+0d) ** MAX_VALUE == +0d

// 11. -0 ** (+anything except 0, NAN, odd integer)  is +0
even_integer().with { even ->
  assert (-0d) ** even == +0d
}

// 12. +0 ** (-anything except 0, NAN)               is +INF
assert (+0d) ** -MIN_VALUE == POSITIVE_INFINITY
assert (+0d) ** -MAX_VALUE == POSITIVE_INFINITY

// 13. -0 ** (-anything except 0, NAN, odd integer)  is +INF
even_integer().with { even ->
  assert (-0d) ** -even == POSITIVE_INFINITY
}

// 14. -0 ** (odd integer) = -( +0 ** (odd integer) )
odd_integer().with { odd ->
  assert (-0d) ** odd == -(0d ** odd)
}

// 15. +INF ** (+anything except 0,NAN) is +INF
assert POSITIVE_INFINITY ** MIN_VALUE         == POSITIVE_INFINITY
assert POSITIVE_INFINITY ** MAX_VALUE         == POSITIVE_INFINITY
assert POSITIVE_INFINITY ** POSITIVE_INFINITY == POSITIVE_INFINITY

// 16. +INF ** (-anything except 0,NAN) is +0
assert POSITIVE_INFINITY ** -MIN_VALUE        == +0d
assert POSITIVE_INFINITY ** -MAX_VALUE        == +0d
assert POSITIVE_INFINITY ** NEGATIVE_INFINITY == +0d

// 17. -INF ** (anything)  = -0 ** (-anything)
assert NEGATIVE_INFINITY ** MIN_VALUE         == (-0d) ** (-MIN_VALUE)
assert NEGATIVE_INFINITY ** MAX_VALUE         == (-0d) ** (-MAX_VALUE)
assert NEGATIVE_INFINITY ** NEGATIVE_INFINITY == (-0d) ** POSITIVE_INFINITY

// 18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer)
integer().with { i ->
  assert (-MIN_VALUE)      ** i == (-1) ** i * (+MIN_VALUE        ** i)
  assert (-MAX_VALUE)      ** i == (-1) ** i * (+MAX_VALUE        ** i)
  assert NEGATIVE_INFINITY ** i == (-1) ** i * (POSITIVE_INFINITY ** i)
}

// 19. (-anything except 0 and inf) ** (non-integer) is NAN
non_integer().with { non_i ->
  assert (-MIN_VALUE) ** non_i == NaN
  assert (-MAX_VALUE) ** non_i == NaN
}

気になった振る舞いは 2 点

  • NaN は 0 乗すると 1 になる
  • 1 を無限大乗すると NaN になる(何故 ?)

-1 の場合は納得できるが 1 の場合は 1 でいいのに。
ちなみに Ruby は 1 も -1 も無限大乗すると 1 になった。


一覧表があったのでこっちの方がわかりやすい。

まとめ

  • Double のゼロと非数は比較したときの振る舞いが primitive と wrapper で違う
  • 1 を無限大乗すると NaN になる

*1:距離関数 のところで分からなかったので