|
|
型変換 |
H.Kamifuji . |
整数と浮動小数点、または整数同士であってもint型とlong型など異なる型の値の演算などが行われる場合があります。この時に型変換と呼ばれる処理が行われます。ここでは型変換がどのように行われるのかを確認していきます。 当ページでは、Linux CentOS7 の Gnome で動作テストしています。 現在(2021/08)では、JDK-16.0.2 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2021/11)では、JDK-17.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2023/04)では、JDK-20.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2024/10)では、JDK-23 にアップされています。一部、上位互換について、見直しを行っていきます。 |
|
型変換は異なるデータ型の間で演算が行われる時に発生します。例えばint型の変数のshort型の値を代入する場合や、int型の値とlong型の値を乗算するような場合です。このような時、いずれかの値がもう片方の値のデータ型に変換が行われます。この時、サイズが小さい型から大きい型へ変換する場合と、逆に大きい型から小さい型へ変換される場合の二通りがあります。 ・サイズが小さい型から大きい型への変換 ・サイズが大きい型から小さい型への変換ここでサイズというのは表現できる数の大きさです。例えばlong型は64ビットでfloat型は32ビットですが、表現できる数の大きさはfloat型の方が大きいためサイズとしてはfloat型の方がlong型よりも大きくなります。(データ型毎に表現できる数については「基本のデータ型」を参照して下さい)。 どのデータ型からどのデータ型に変換した時、どちらの変化が行われるのかは次の通りです。 ![]() int型とlong型で見てみれば、int型をlong型に変換する場合は小さい型から大きい型への変換となり、逆にlong型をint型に変換する場合は大きい型から小さい型への変換となります。またint型とdouble型で見た場合もint型をdouble型に変換する場合は小さい型から大きい型への変換となり、逆にdouble型をint型に変換する場合は大きい型から小さい型への変換となります。 基本データ型の中でもboolean型は特別でboolean型から他のデータ型に変換はできず、また逆にboolean型に変換することもできません。 注意すべき点はshort型とchar型の場合、またbyte型とchar型の場合などです。この場合はどちらに変換する場合も大きい型から小さい型への変換という扱いになります。詳細は別のページで解説します。 サイズが大きい型から小さい型へ変換した場合、例えば4桁の数値しか入らないところに6桁の数値を入れようとしても入らないように場合によっては元の値のまま格納できない場合もあります。また小さい型から大きい型へ変換する場合も、整数から浮動小数点数に変換する場合に元のままの値ではなくなる場合もあります。 では次項のページ以降で型変換を行う具体的な方法について確認していきます。 |
short型からint型への変換や、int型からlong型への変換などサイズが小さい型から大きい型への変換の場合、より大きな数値が格納できるところへ変換するわけですから特に意識することなく変換することができます。 次の例を見てください。 short s = 10; int i = s; long l = i;2行目ではshort型の値をint型の変数に代入しています。この時、short型の値はint型に変換されてから代入されることになります。 同じように3行目ではint型の値をlong型の変数に代入しています。この時、int型の値はlong型に変換されてから代入されることになります。 このようにサイズが小さい型から大きい型への変換の場合は特に意識することなく自動的に変換が行われますし値そのものは変わりがありません。short型の10という値はint型に変換されてもint型の10になるだけです。ただし、整数から浮動小数点数に変換する場合は注意が必要です。 long型からfloat型やdouble型に変換する場合の注意点例えばfloat型の値は負の値は -3.4028235E+38 〜 -1.401298E-45、正の値は 1.401298E-45 〜 3.4028235E+38 の範囲の値を表現できます。ただし、float型やdouble型には有効桁数というものがあり、float型ではおよそ10進数で7桁(2進数で24桁)、double型では10進数でおよそ16桁(2進数で53桁)となっています。有効桁数とは表示された数値の中で信頼できる値が何桁あるのかということであり、もしも有効桁数が3桁であれば12.345という数値で信頼できるのは12.3まででそれ以降の数値には誤差が含まれている可能性があります。また1.2345という数値であれば信頼できるのは1.23まででそれ以降の数値には誤差が含まれている可能性があります。 このため、long型の値で7桁より大きい数値をfloat型の値に変換すると変換は行われますが7桁以降の数には誤差が含まれる可能性があります。同じように15桁以上の大きい数値をdouble型の値に変換した場合も誤差が含まれる可能性があります。 次の例を見てください。 long l = 1234567890L; float f = l;この場合、変換は行われますが変換前と変換後の値を画面に出力すると異なる結果となります。具体的には後のサンプルを見てください。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample2_1{ public static void main(String args[]){ short s = 123; int i = s; System.out.println("s = " + s); System.out.println("i = " + i); long l = 123456789012345678L; float f = l; double d = l; System.out.println("l = " + l); System.out.println("f = " + f); System.out.println("d = " + d); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Cast]$ java JSample2_1 s = 123 i = 123 l = 123456789012345678 f = 1.23456791E17 d = 1.2345678901234568E17 [xxxxxxxx@dddddddddd Cast]$long型の値をfloat型やdouble型に変換した場合、有効桁数を超えた部分については元の値と異なっていることが分かります。 |
サイズが小さい型から大きい型へ変換する場合、単に代入を行うだけで自動的に型の変換が行われていましたが、型の変換を明示的に行う場合にはキャスト演算子を使います。 キャスト演算子の書式は次の通りです。 (データ型)式変換したいデータ型を「(」と「)」の間に記述すると右辺に書かれた式の型を変換することができます。 例えば次のように記述します。 int i = 10; long l = (long)i;この例ではint型の変数iをキャスト演算子を使ってlong型に変換してから代入を行っています。この場合は代入するよりも前の時点で既に右辺の値はlong型となっています。 前のページで見た通りサイズが小さい型から大きい型へ代入を行う場合は明示的にキャスト演算子を使用する必要はありませんが、逆にサイズが大きい型から小さい型への変換など必ずキャスト演算子を使ってデータ型を変換する必要がある場合もあります。 なお式の中で演算の優先順位を変更する場合にも「(」と「)」を使いますが、括弧の中がデータ型だった場合はキャスト演算子となります。キャスト演算子の優先順位は比較的高いので記述する場合はどの部分にキャスト演算子が影響するのか注意して下さい。(演算子の優先順位については「演算子の優先順位と結合規則」を参照下さい)。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample3_1{ public static void main(String args[]){ int i = 7; float f1 = (float)i / 3; float f2 = (float)(i / 3); System.out.println("f1 = " + f1); System.out.println("f2 = " + f2); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Cast]$ java JSample3_1 f1 = 2.3333333 f2 = 2.0 [xxxxxxxx@dddddddddd Cast]$最初の計算式では変数iをfloat型に変換してから3で除算した結果を変数f1へ代入しています。2番目の計算式では変数iを3で除算した結果をfloat型に変換してから変数f2で代入しています。このように演算の内容によっては結果が異なる場合があります。詳しい解説は後のページで行います。 |
int型からshort型への変換や、long型からint型への変換などサイズが大きい型から小さい型への変換の場合、キャスト演算子を使って明示的に型が変更されることを示す必要があります。 まず間違った記述です。次の例を見てください。 int i = 10; short s = i;int型の値をshort型の値に単に代入しています。このプログラムをコンパイルすると「精度が落ちている可能性」というコンパイルエラーが表示されます。このようにサイズが大きい型から小さい型へ変換を行う場合はキャスト演算子が必須となります。 よって次のように記述する必要があります。 int i = 10; short s = (short)i;キャスト演算子を使うことによってエラーは発生せずに型の変換が行われます。 数値リテラルを指定した場合の特例単に整数を記述した場合はint型の値として扱われます。よって次のように記述した場合はint型の値をキャストせずにshort型の変数に代入しようとすることになります。short s = 10;これはサイズが大きい型から小さい型への変換となるのでエラーとなるはずですが、数値そのものをshort型またはbyte型に代入した場合はキャストを行わなくてもエラーとなりません。この場合は自動的に型が変換されます。 ただし自動的に型が変換されるのはshort型またはbyte型の範囲内にある整数を代入しようとした場合だけです。次の例のようにshort型の範囲を超えた整数を直接代入しようとすると「精度が落ちている可能性」というコンパイルエラーが発生します。 short s = 345321; 変換によって数値の情報が失われる場合大きなサイズの型から小さい型への変換の場合、変換の対象となる数値が変換後のデータ型が扱える範囲を超えてしまうと正しい数値として変換が行えなくなります。次の例を見てください。 int i = 10; short s = (short)i;この場合、int型の変数に格納された数値の10は、変換後のshort型でも扱うことができます。その為、変換後の値も10のままです。 では次の例を見てください。 int i = 345321; short s = (short)i;この場合、int型の変数に格納された数値の345321は、変換後のshort型で扱える範囲を超えてしまいます。その為、short型の変数にはどんな数値が格納されるのかは分かりません。 また浮動小数点数から整数へ変換する場合には、整数では小数点以下の値は扱えませんので変換後は切り捨てられます。次の例を見てください。 double d = 12.345; int i = (int)d;この場合、float型の数値をint型の数値に変換していますが、int型は整数した扱えない為に変換前の値12.345のうち小数点以下が切り捨てられて12がint型の変数に格納されます。 byte型、short型、char型の注意点「型変換の基本ルール」の一覧表を見て頂くと分かりますがshort型からchar型への変換、逆にchar型からshort型への変換がどちらも大きいサイズから小さいサイズへの変換となっています。short型とchar型はどちらも16ビットのデータ型なのですが、取り扱うことができる値の範囲が異なります。char 0〜65535 short -32768〜32767例えばchar型の40000という数値をshort型に変換すると情報が失われてしまいます。またshort型の-300という数値をchar型に変換すると情報が失われてしまいます。 またbyte型からchar型に変換する場合、byte型は8ビットでありchar型は16ビットなのですがbyte型は負の値を扱えるのに対してchar型は正の値しか扱う事ができません。 char 0〜65535 byte -128〜128例えばbyte型の-17という数値をchar型に変換すると情報が失われてしまいます。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample4_1{ public static void main(String args[]){ int i = 345321; short s = (short)i; System.out.println("i = " + i); System.out.println("s = " + s); i = 1829; s = (short)i; System.out.println("i = " + i); System.out.println("s = " + s); double d = 12.345; i = (int)d; System.out.println("d = " + d); System.out.println("i = " + i); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Cast]$ java JSample4_1 i = 345321 s = 17641 i = 1829 s = 1829 d = 12.345 i = 12 [xxxxxxxx@dddddddddd Cast]$ |
今までは「=」を使った代入演算が行われる時の型変換について見てきました。ここでは「+」や「*」など2つの値を対象とした二項演算の際の型変換ルールについて確認します。 型変換は次の規則に従います。 (1) どちらかの値がdouble型の場合は他の値をdouble型に変換する (2) どちらかの値がfloat型の場合は他の値をfloat型に変換する (3) どちらかの値がlong型の場合は他の値をlong型に変換する (4) (1)から(3)に該当しない場合は両方の値をint型に変換するこの規則は(1)から順に適用され、いずれかに適用されればその後の変換は行われません。例えばdouble型とlong型の値の演算では、long型の値がdouble型に変換されて演算が行われます。 次の例を見てください。 double d = 12.345; long l = 3220L; double val = d * l;上記ではdouble型の変数「d」とlong型の変数「l」が「*」演算子によって演算さえる際、まずlong型の変数「l」がdouble型に変換された後で演算が行われます。結果として「d * l」はdoule型となりその後で「=」演算子によって左辺の変数「val」に代入されます。 もう1つ例を見てみます。 int i = 83; double d = 12.345; long l = 3220L; double val = d + i * l;複数の演算子が組み合わされた場合も演算子の優先順位に従って順に演算が行われます。まず「i * l」が行われますが演算が行われる前に変数「i」がlong型に変換されます。結果として「i * l」はlong型の値となります。次に「d」と「i * l」の演算が行われますが演算が行われる前に「i * l」の結果がdouble型に変換されます。結果として「d + i * l」はdouble型の値となります。 int型への変換に伴う注意点double型、float型、long型に該当しない値は全てint型に変換されてから演算が行われます。つまりshort型とshort型の演算を行う場合もどちらの値もいったんint型に変換されてから演算が行われると言う事です。次の例を見てください。 short s1 = 10; short s2 = 9; short val = s1 + s2;一見するとshort型とshort型の値を演算した結果をshort型の変数に代入しているので問題無いように見えますが、コンパイルすると「精度が落ちている可能性」というコンパイルエラーが表示されます。 これは演算子の対象の値がどちらもdouble型、float型、long型に該当しませんので、演算が行われる前にどちらの値もint型に変換されてから演算が行われます。結果として「s1 + s2」はint型の値となります。int型の値をshort型の値に代入する場合はキャスト演算子が必要となるためコンパイルエラーが発生します。 正しくは次のように記述する必要があります。 short s1 = 10; short s2 = 9; short val = (short)(s1 + s2);なぜエラーとなっているか分かりにくい箇所なので注意して下さい。 単項マイナス演算子の注意点単項マイナス演算子を使うことで符号を反転することができます。(詳しくは「単項マイナス演算子」を参照して下さい)。int n1, n2; n1 = 10; n2 = -n1;ただしbyte型、short型、char型に対して単項マイナス演算子を使用する時は注意が必要です。byte型、short型、char型の場合には符号を反転させる前に対象の値をint型へ自動的に型変換するためです。その為、次のような記述を行うとコンパイル時に「精度が落ちている可能性」というコンパイルエラーが表示されます。 short s1 = 10; short s2 = -s1;これは単項マイナス演算子によって「s1」がint型へ変換され、結果的に「-s1」がint型の値になっているためです。int型の値をshort型の値に代入する場合はキャスト演算子が必要となります。 正しくは次のように記述する必要があります。 short s1 = 10; short s2 = (short)-s1;これも分かりにくいエラーが発生するところなので注意して下さい。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample5_1{ public static void main(String args[]){ double d1 = 7.0; int i1 = 7; int i2 = 3; System.out.println(d1 / i2); System.out.println(i1 / i2); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Cast]$ java JSample5_1 2.3333333333333335 2 [xxxxxxxx@dddddddddd Cast]$ |
|