キャストと変換について理解すると思うたびに、別の奇妙な動作を見つけます。
long l = 123456789L;
float f = l;
System.out.println(f); // outputs 1.23456792E8
long
のビット深度がfloat
よりも大きい場合、これをコンパイルするには明示的なキャストが必要になると思います。そして当然のことながら、結果の精度が失われています。
ここでキャストが不要なのはなぜですか?
long
からdouble
に同じ質問をすることができます-両方の変換で情報が失われる可能性があります。
Java言語仕様 のセクション5.1.2はこう述べています:
プリミティブ変換を拡大しても、数値の全体的な大きさに関する情報は失われません。実際、整数型から別の整数型に拡張された変換では、情報がまったく失われません。数値は正確に保持されます。 strictfp式でfloatからdoubleに広がる変換でも、数値が正確に保持されます。ただし、strictfpではないこのような変換では、変換された値の全体的な大きさに関する情報が失われる可能性があります。
Intまたはlong値をfloatに変換したり、long値をdoubleに変換したりすると、精度が失われる可能性があります。つまり、値の最下位ビットの一部が失われる可能性があります。この場合、結果の浮動小数点値は、IEEE 754の四捨五入モード(§4.2.4)を使用して、整数値を正しく丸めたものになります。
つまり、情報が失われる可能性があっても、値はターゲットタイプの全体的な範囲内にあることがわかります。
確かに、すべての暗黙の変換が情報をまったく失わないように選択することもできます。そのため、int
およびlong
からfloat
は明示的であり、long
からdouble
は明示的でした。 (int
からdouble
までは問題ありません。double
には、すべてのint
値を正確に表すのに十分な精度があります。)
いくつかのケースではそれは有用だったでしょう-いくつかのケースでは役に立たなかった。言語設計は妥協についてです。あなたはそれらすべてを獲得することはできません。どんな決断をしたかわからない...
Java言語仕様、第5章:変換とプロモーション はこの問題に対処します。
5.1.2プリミティブ変換の拡大
プリミティブ型に関する次の19の特定の変換は、拡大プリミティブ変換と呼ばれます。
- byteからshort、int、long、float、またはdouble
- shortからint、long、float、またはdouble
- charからint、long、float、またはdouble
- intからlong、float、またはdouble
- 浮動または倍長
- 倍に浮動
プリミティブ変換を拡大しても、数値の全体的な大きさに関する情報は失われません。
...
Intまたはlong値をfloatに変換したり、long値をdoubleに変換したりすると、精度が失われる可能性があります。つまり、値の最下位ビットの一部が失われる可能性があります。この場合、結果の浮動小数点値は整数値の正しく丸められたバージョンになります
言い換えると、JLSはmagnitudeの損失と精度。
たとえば、int
からbyte
は、byte
に500を格納できないため、(潜在的な)損失になります。
long
〜float
は、floatの値の範囲がlongの値の範囲よりも大きいため、精度が失われる可能性がありますが、大きさではありません。
したがって、ルールは次のとおりです。
微妙?承知しました。しかし、それでそれが明らかになることを願っています。
Longはfloatよりも内部的に多くのビットを使用することは正しいですが、Java言語は拡張パスで機能します。
バイト->ショート->整数->ロング->フロート->ダブル
左から右に変換する(拡大変換)には、キャストは必要ありません(そのため、フロートを長くすることができます)。右から左に変換するには(ナロー変換)、明示的なキャストが必要です。
どこかで聞いたことがあります。 Floatは、そのままの形で指数形式で格納できます。 「23500000000」は「2.35e10」として格納されます。したがって、floatにはlongの値の範囲を占有するスペースがあります。指数形式で格納することも、精度が失われる理由です。