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はすでにバグレポートを送信しています。

Source: https://habr.com/ru/post/J112948/


All Articles