Javaが2.2250738585072012e-308でハングする

Konstantin Preisserは最近、非常に興味深いものを発見しました。Java(ランタイムとコンパイラーの両方)は、10進数2.2250738585072012e-308



をdoubleに変換するときに無限ループに入ります。 理論的には、数値は0x1p-1022



、つまり0x1p-1022



に変換する必要があります。 ただし、Javaはdoubleの最大の非正規化数である0x0.fffffffffffffp-1022



でフリーズします。



実行時の無限ループ

 class RuntimeHang { public static void main(String[] args) { System.out.println("Test:"); double d = Double.parseDouble("2.2250738585072012e-308"); System.out.println("Value: " + d); } }
      
      







コンパイル時の無限ループ



Eclipseで試してみたい場合は、最初にすべてを保存することを忘れないでください。そうしないと、シャドウコンピレーションで感覚をつかむことができません
 class CompilationHang { public static void main(String[] args) { double d = 2.2250738585072012e-308; System.out.println("Value: " + d); } }
      
      





猫の下で、この現象の原因に関する著者の推論。



問題は何ですか?



Konstantinは、少なくとも実行時には、問題はFloatingDecimal.javaの 「修正サイクル」にあることを発見しました。 彼はこう書いている:



この部分をコメントアウトすると、 sun.misc.FloatingDecimal.readJavaFormatString(s).doubleValue()



を呼び出すsun.misc.FloatingDecimal.readJavaFormatString(s).doubleValue()



Double.parseDouble(String s)



は純粋なJavaコードであり、ネイティブではないため、 Double.parseDouble(String s)



にハングアップはありません。 ただし、浮動小数点演算を使用するため、JREとjavacがコンパイルされたコンパイラ設定になる可能性があります。



修正サイクルがない場合、そのようなビット(ビッグエンディアン)が出力されます。

 00000000 00001111 11111111 11111111 11111111 11111111 11111111 11111111
      
      





つまり、指数はゼロであるため、数値は最大の非正規化浮動小数点数に変換されます。 修正サイクルがないと、 2.2250738585072013e-308



同じことが起こりますが、サイクルのコメントを2.2250738585072013e-308



、正しく変換されます:

 00000000 00010000 00000000 00000000 00000000 00000000 00000000 00000000
      
      









翻訳者から



32ビットおよび64ビットのHotSpotおよび64ビットのOpenJDKで問題を確認して再現しました。 さらに、 PHPにも同様の問題があります 。 Konstantin Preisserはすでにバグレポートを送信しています。



All Articles