akios @ ウィキ
5.1.3. 縮幅プリミティブ変換
最終更新:
akios
-
view
5. 変換と昇格
5.1. 変換の種類
5.1.1. 恒等変換
5.1.2. 拡幅プリミティブ変換
5.1.3. 縮幅プリミティブ変換
プリミティブ型に対する22の特定の変換が縮幅プリミティブ変換(narrowing primitive conversion)と呼ばれます。:
- shortをbyteやcharへ変換
- charをbyteやshortへ変換
- intをbyteやshort、charへ変換
- longをbyteやshort、char、intへ変換
- floatをbyteやshort、char、int、longへ変換
- doubleをbyteやshort、char、int、long、floatへ変換
縮幅プリミティブ変換は数値の全体的な大きさという情報を失うかもしれません。また、精度や範囲についても失うかもしれません。
doubleからfloatへの縮幅プリミティブ変換はIEEE754の丸め規則に従います。この変換では精度や範囲も失う可能性があり、非ゼロdoubleからfloatゼロという結果や有限doubleからfloat無限大という結果を生む可能性があります。DoubleNaNはfloatNaNに変換されます。double無限大は同じ符号のfloat無限大に変換されます。
符号付き整数から整数型Tへの縮幅変換は、nを型Tを表すのに必要なビット数とすると、下位nビット以外を単純に切り捨てます。数値の大きさに関する情報を失う可能性に加えて、入力値の符号と異なる符号に変換される可能性があります。
charから整数型Tへの縮幅変換は、同様に、nを型Tを表すのに必要なビット数とすると、下位nビット以外を単純に切り捨てます。数値の大きさに関する情報を失う可能性に加えて、文字は符号なし16ビット整数値で表されるのに、変換結果が負の数となる可能性があります。
浮動小数点数から整数型Tへの縮幅変換は以下の2ステップで行われます。:
- 第1ステップで、浮動小数点数はTがlongならlongに、Tがbyteやshort、char、intならintに次のように変換されます。:
- 浮動小数点数がNaNなら、変換の第1ステップの結果はintかlongの0となります。
- そうでなく、浮動小数点数が無限大でなければ、浮動小数点値はIEEE754のゼロに向かう丸めモードを使用してゼロ方向へ丸められ、整数値Vへと丸められます。
- もしTがlongでこの整数値がlongで表されるなら、第1ステップの結果はlong値Vとなります。
- そうでなく、この整数値がintで表されるなら、第1ステップの結果はint値Vとなります。
- そうでなければ、以下の2ケースのどちらかが真となります。:
- 値が小さすぎる(負の大きすぎる値や負の無限大)ので、第1ステップの結果は型intかlongが表現できる最小の値となります。
- 値が大きすぎる(正の大きすぎる値や正の無限大)ので、第1ステップの結果は型intかlongが表現できる最大の値となります。
- 第2ステップでは以下が行われます。:
- Tがintやlongならば、第1ステップの結果が変換の結果となります。
- Tがbyteやcharやshortならば、第1ステップの結果から型Tへ縮幅変換の結果が変換の結果となります。
class Test { public static void main(String[] args) { float fmin = Float.NEGATIVE_INFINITY; float fmax = Float.POSITIVE_INFINITY; System.out.println("long: " + (long)fmin + ".." + (long)fmax); System.out.println("int: " + (int)fmin + ".." + (int)fmax); System.out.println("short: " + (short)fmin + ".." + (short)fmax); System.out.println("char: " + (int)(char)fmin + ".." + (int)(char)fmax); System.out.println("byte: " + (byte)fmin + ".." + (byte)fmax); } }
このプログラムは以下を出力します。:
long: -9223372036854775808..9223372036854775807 int: -2147483648..2147483647 short: 0..-1 char: 0..65535 byte: 0..-1
charやintやlongは驚くべきことではありません。その型が取りうる最小値と最大値が出力されただけです。
byteとshortは符号と数値の大きさに関する情報が失われており、精度も失っています。結果はintの最大値と最小値の下位ビットだけと考えれば理解できます。最小値のintは16進で0x80000000で、最大値は0x7fffffffです。これはshortの場合を考えると下位16ビットだけとなるので、それぞれ0x0000と0xffffとなります。charの場合を考えると同じく下位16ビットだけとなるので、それぞれ'\u0000'と'\uffff'となります。byteの場合を考えると下位8ビットだけとなるので、それぞれ0x00と0xffとなります。
オーバーフローやアンダーフローやその他の情報の損失を引き起こすにもかかわらず、縮幅プリミティブ変換は実行時に決して例外を発生しません。
class Test { public static void main(String[] args) { // A narrowing of int to short loses high bits: System.out.println("(short)0x12345678==0x" + Integer.toHexString((short)0x12345678)); // A int value not fitting in byte changes sign and magnitude: System.out.println("(byte)255==" + (byte)255); // A float value too big to fit gives largest int value: System.out.println("(int)1e20f==" + (int)1e20f); // A NaN converted to int yields zero: System.out.println("(int)NaN==" + (int)Float.NaN); // A double value too large for float yields infinity: System.out.println("(float)-1e100==" + (float)-1e100); // A double value too small for float underflows to zero: System.out.println("(float)1e-50==" + (float)1e-50); } }
このプログラムは以下を出力します。:
(short)0x12345678==0x5678 (byte)255==-1 (int)1e20f==2147483647 (int)NaN==0 (float)-1e100==-Infinity (float)1e-50==0.0