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 になる