型キャスト

算術演算子に付随してプリミティブ型の型キャストについて理解を深める必要があります。

まずは、前頁で出題した簡単な計算の答え合わせをしていきましょう。

[結果]次の計算の答えはいくつでしょう。

桁溢れサンプル1
    //intの最大値(2147483647)
    int intMax = Integer.MAX_VALUE;
    //intの最小値(-2147483648)
    int intMin = Integer.MIN_VALUE;

    int resultA = intMax + 1;
    System.out.println("2147483647 + 1 = " + resultA);
    int resultB = intMin - 1;
    System.out.println("-2147483648 - 1 = " + resultB);
					
実行結果
    2147483647 + 1 = -2147483648
    -2147483648 - 1 = 2147483647
					

resultAには、10進法の加算で期待していた結果「2147483648」にならずに符号が反転して、intの最小値「-2147483648」になりました。
同様にresultBには、期待していた「-2147483649」に符号が判定して、intの最大値「2147483647」になりました。

これが企業の会計における決算処理であれば、21億の黒字が21億の赤字と計算されてしまう事になってしまいます。

算術演算子の計算処理はとても簡単に表現できますが、算術演算によって桁あふれや少数点の丸めなどに注意する必要があります。

これを改善しようと結果の変数resultA及びresultBをintより大きいlongにするとどうなるでしょうか?

桁溢れのサンプル2

結果である変数をintからlongに変更することが最初に浮かばれた事と思います。

桁溢れサンプル2
    //intの最大値(2147483647)
    int intMax = Integer.MAX_VALUE;
    //intの最小値(-2147483648)
    int intMin = Integer.MIN_VALUE;
    long resultA2 = intMax + 1;
    System.out.println("2147483647 + 1 = " + resultA2);
    long resultB2 = intMin - 1;
    System.out.println("-2147483648 - 1 = " + resultB2);
					
実行結果
    2147483647 + 1 = -2147483648
    -2147483648 - 1 = 2147483647
					

残念ながら実行結果に変わりはありません。
これは、右の数式( intMax + 1) の評価式はintで評価されているからintの数値範囲を超えると桁溢れした結果、
符号が反転されて計算されています。これを回避するには、以下の2通りがあります。

  • 算術演算するintMax変数をlongにして、等式の左右を同じ型にする。
  • 型キャストをしてlong型として解釈させる。

実際には、ビジネスロジックを記載するときに1.のケースは少し大雑把なやり方です。
算術演算子右側に出現するintMax変数は、設計時にintの範囲で収まる桁数だからintを採用しているので、それをlongにすると意味合いが変わってきます。
変数がintということは、約21億~-21億の数値の範囲であることの意味です。変数がlongということは、約922京までの範囲です。
これを計算前のintMax変数をlongにすることは、変数の値が21億以上の値が必ず存在しうることを意味します。

処理要件として異なった意味になってしまいます。正しい設計の上で正しく実行するには、次のように型キャストを行います。

型キャストを用いて
    //intの最大値(2147483647)
    int intMax = Integer.MAX_VALUE;
    //intの最小値(-2147483648)
    int intMin = Integer.MIN_VALUE;
    long resultA3 = (long)intMax + 1;
    System.out.println("2147483647 + 1 = " + resultA3);
    long resultB3 = (long)intMin - 1;
    System.out.println("-2147483648 - 1 = " + resultB3);
                    
実行結果
    2147483647 + 1 = 2147483648
    -2147483648 - 1= -2147483649
					

適切な型キャストにすることにより期待する結果が得られました。以下に算術演算子における型キャストが重要かご理解頂けたかと思います。

プリミティブ型の型キャスト

プリミティブ型の型キャストの構文及び桁溢れしないための変換ルールについて説明します。

プリミティブ型の型キャスト構文

プリミティブ型の型キャスト構文
処理結果の型 変数名 = (処理結果の型)変換前の変数
型キャストの例
    //intの最大値(2147483647)
    int intMax = Integer.MAX_VALUE;
    long result = (long)intMax + 1;
								

プリミティブ型の型キャストには、Oracle社本家サイト「変換とコンテキスト」において、
以下の2つの内容が定義されています。

  1. 値の範囲拡大のためのプリミテイブ型変換(Widening Primitive conversions)

    小さいサイズのプリミティブ型から、大きいサイズのプリミテイブ型へ変換することを意味します。

  2. 値の範囲縮小のためのプリミテイブ型変換(Narrowing Primitive Conversion)

    大きいサイズのプリミティブ型から、小さいサイズのプリミテイブ型へ変換することを意味します。

値の範囲拡大のためのプリミテイブ型変換(Widening Primitive conversions)

下記一覧表のとおり、値の範囲拡大のためのプリミテイブ型変換では、19種類の変換が可能です。

当サイトの「プリミティブ型」8つの基本データ型の数値範囲をご覧ください。

変換前のプリミティブ型は、変換前の数値範囲より大きい範囲を表現できるプリミティブ型に変換できるという事です。

変換前プリミティブ型 変換先のプリミティブ型
byte short , int , long , float , double
short int , long , float , double
char int , long , float , double
int long, float, double
long float , double
float double

重要以下のルールに則りプリミティブ型の変換を行った場合には、変換前の数値の情報が欠ける事無く変換されます。

  • 整数型(byte,short,char,int,long)から整数値(一覧表上の変換先)への変換する場合
  • byte,short,charから浮動小数点型(float,dobule)への変換する場合
  • intからdoubleへの変換する場合
  • strictfp修飾子を利用して、floatからdobuleに変換する場合

上記4つに該当しない場合には、桁溢れによる丸め処理が行われ、正確な数値結果を得られません。

上記4つに該当しない浮動小数点型への変換には、IEEE 754(IEEE 浮動小数点数演算標準)に近い丸めモードを使用して数値の丸め処理が行われます。

4.項のstrictfp修飾子を利用するケースは、研究所(宇宙、バイオテクノロジー、高度な物理、数学式で精度を要求される分野等)や原子力発電、精密機器等の精密な計算処理を必要とする分野で利用されるでしょう。
一般企業向けのビジネスシステムでは、利用されることはまずありません。

重要丸め処理が発生するケースの場合でもプリミテイブ型の変換では、コンパイルエラーが発生しません。
そのため、丸め処理が発生する可能性のある計算処理では、設計、製造、特に単体試験よる乖離値チェックにより丸めによる処理結果としての問題が無いかチェックする必要があります。

値の範囲縮小のためのプリミテイブ型変換(Narrowing Primitive conversions)

下記一覧表のとおり、値の範囲拡大のためのプリミテイブ型変換では、22種類の変換が可能です。

当サイトの「プリミティブ型」8つの基本データ型の数値範囲をご覧ください。

変換前のプリミティブ型は、変換前の数値範囲より小さい範囲を表現できるプリミティブ型に変換できるという事です。
数値範囲の大きい数値から小さい数値へは値は入りきらないので、必然的に丸め処理が起きます。

変換前プリミティブ型 変換先のプリミティブ型
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への変換には、IEEE 754(IEEE 浮動小数点数演算標準)の丸め規則に準じて数値の丸め処理が行われます。

2017/02/25記。このようなケースはどのようなケースで利用されるか分かり次第追記致します。
私自身の15年程度の経験では、値の範囲縮小のためのプリミテイブ型変換を利用したことが恐らくなかったです。

最後に及び参考文献

オブジェクト型に関する型キャストに関しては、オブジェクトで継承の講座以降に説明いたします。

値の範囲拡大はintの約+-21億を超える範囲の計算や少数点が必要とされるときに、これは桁溢や丸め起こりえるかもと少しでも考えて頂ければ良いかと思います。

参考文献は英語ですが、日本語に訳されるより正確な情報で理解し易いです。Google翻訳を利用するとかなり精度の高い結果がでます。