akios @ ウィキ

5.1.3. 縮幅プリミティブ変換

最終更新:

akios

- view
管理者のみ編集可

5. 変換と昇格

5.1. 変換の種類

5.1.1. 恒等変換

5.1.2. 拡幅プリミティブ変換

5.1.3. 縮幅プリミティブ変換

プリミティブ型に対する22の特定の変換が縮幅プリミティブ変換(narrowing primitive conversion)と呼ばれます。:

  • shortbytecharへ変換
  • charbyteshortへ変換
  • intbyteshortcharへ変換
  • longbyteshortcharintへ変換
  • floatbyteshortcharintlongへ変換
  • doublebyteshortcharintlongfloatへ変換

縮幅プリミティブ変換は数値の全体的な大きさという情報を失うかもしれません。また、精度や範囲についても失うかもしれません。

doubleからfloatへの縮幅プリミティブ変換はIEEE754の丸め規則に従います。この変換では精度や範囲も失う可能性があり、非ゼロdoubleからfloatゼロという結果や有限doubleからfloat無限大という結果を生む可能性があります。DoubleNaNはfloatNaNに変換されます。double無限大は同じ符号のfloat無限大に変換されます。

符号付き整数から整数型Tへの縮幅変換は、nを型Tを表すのに必要なビット数とすると、下位nビット以外を単純に切り捨てます。数値の大きさに関する情報を失う可能性に加えて、入力値の符号と異なる符号に変換される可能性があります。

charから整数型Tへの縮幅変換は、同様に、nを型Tを表すのに必要なビット数とすると、下位nビット以外を単純に切り捨てます。数値の大きさに関する情報を失う可能性に加えて、文字は符号なし16ビット整数値で表されるのに、変換結果が負の数となる可能性があります。

浮動小数点数から整数型Tへの縮幅変換は以下の2ステップで行われます。:
  1. 第1ステップで、浮動小数点数はTがlongならlongに、Tがbyteshortcharintならintに次のように変換されます。:
    1. 浮動小数点数がNaNなら、変換の第1ステップの結果はintlong0となります。
    2. そうでなく、浮動小数点数が無限大でなければ、浮動小数点値はIEEE754のゼロに向かう丸めモードを使用してゼロ方向へ丸められ、整数値Vへと丸められます。
      1. もしTがlongでこの整数値がlongで表されるなら、第1ステップの結果はlong値Vとなります。
      2. そうでなく、この整数値がintで表されるなら、第1ステップの結果はint値Vとなります。
    3. そうでなければ、以下の2ケースのどちらかが真となります。:
      1. 値が小さすぎる(負の大きすぎる値や負の無限大)ので、第1ステップの結果は型intlongが表現できる最小の値となります。
      2. 値が大きすぎる(正の大きすぎる値や正の無限大)ので、第1ステップの結果は型intlongが表現できる最大の値となります。
  2. 第2ステップでは以下が行われます。:
    1. Tがintlongならば、第1ステップの結果が変換の結果となります。
    2. Tがbytecharshortならば、第1ステップの結果から型Tへ縮幅変換の結果が変換の結果となります。

例5.1.3-1. 縮幅プリミティブ変換

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

charintlongは驚くべきことではありません。その型が取りうる最小値と最大値が出力されただけです。

byteshortは符号と数値の大きさに関する情報が失われており、精度も失っています。結果はintの最大値と最小値の下位ビットだけと考えれば理解できます。最小値のintは16進で0x80000000で、最大値は0x7fffffffです。これはshortの場合を考えると下位16ビットだけとなるので、それぞれ0x00000xffffとなります。charの場合を考えると同じく下位16ビットだけとなるので、それぞれ'\u0000''\uffff'となります。byteの場合を考えると下位8ビットだけとなるので、それぞれ0x000xffとなります。

オーバーフローやアンダーフローやその他の情報の損失を引き起こすにもかかわらず、縮幅プリミティブ変換は実行時に決して例外を発生しません。

例5.1.3-2. 情報を損失する縮幅プリミティブ変換

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

5.1.4. 拡幅と縮幅プリミティブ変換

5.1.5. 拡幅参照変換

5.1.6. 縮幅参照変換

5.1.7. ボックス化変換

5.1.8. ボックス化解除変換

5.1.9. 未検査変換

5.1.10. 捕捉変換

5.1.11. 文字列変換

5.1.12. 禁止変換

5.1.13. 値集合変換

5.2. 代入変換

5.3. メソッド呼び出し変換

5.4. 文字列変換

5.5. キャスト変換

5.6. 数値昇格

目安箱バナー