2015.12.10
オーバーフロー・アンダーフロー
はじめに
ここでのオーバーフロー(overflow)とは、計算した結果が変数の格納最大値を超えることです。反対に、アンダーフロー(underflow)は変数の格納最小値を下回ることです。
これは計算によって得られる理論値を変数に格納できないことを意味していますが、その場合でも変数には何らかの値が格納されます。
Javaソースコード
Overflow.java
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042
public class Overflow { public static void main( String[] args ) { // 変数宣言 byte a; // 126に1を3回足す a = 126; System.out.println( "理論値=126 変数値=" + a ); ++ a; System.out.println( "理論値=127 変数値=" + a ); ++ a; System.out.println( "理論値=128 変数値=" + a ); ++ a; System.out.println( "理論値=129 変数値=" + a ); System.out.println(); // 変数宣言 short b, c, d; b = 50; c = 200; d = (short)( b * c ); System.out.println( "理論値=10000 変数値=" + d ); b = 500; c = 200; d = (short)( b * c ); System.out.println( "理論値=100000 変数値=" + d ); System.out.println(); // 変数宣言 double e, f, g; e = 100.0; f = 0.0; g = e / f; System.out.println( "理論値=解なし 変数値=" + g ); System.out.println(); } }
コンパイル ソースコードが「ANSI」の場合
C:\talavax\javasample>javac -encoding sjis Overflow.java
コンパイル ソースコードが「UTF-8」の場合
C:\talavax\javasample>javac Overflow.java
実行
C:\talavax\javasample>java Overflow
出力結果
理論値=126 変数値=126 理論値=127 変数値=127 理論値=128 変数値=-128 理論値=129 変数値=-127 理論値=10000 変数値=10000 理論値=100000 変数値=-31072 理論値=解なし 変数値=Infinity
Javaソースコードの解説
ここから順番にソースを解説していきます。
003 004 005 006 007 008 009 010 011 012 013 014 015
// 変数宣言 byte a; // 126に1を3回足す a = 126; System.out.println( "理論値=126 変数値=" + a ); ++ a; System.out.println( "理論値=127 変数値=" + a ); ++ a; System.out.println( "理論値=128 変数値=" + a ); ++ a; System.out.println( "理論値=129 変数値=" + a ); System.out.println();
126を代入したbyte型の変数aに1ずつ足していき、理論値と変数値を並べて表示しています。その結果、127までは理論値と変数値が同じ値となり、理論値=128で変数値=-128と違う結果になってしまいました。これはbyte型に格納できる値の範囲が-128~127であるため、変数に128が格納できないためです。なぜ、127に1を足した値が-128になるかの説明はここでは省略します。
018 019 020 021 022 023 024 025 026 027 028 029 030
// 変数宣言 short b, c, d; b = 50; c = 200; d = (short)( b * c ); System.out.println( "理論値=10000 変数値=" + d ); b = 500; c = 200; d = (short)( b * c ); System.out.println( "理論値=100000 変数値=" + d ); System.out.println();
short型の変数bと変数cに代入した値どうしを掛け算し、その結果をshort型の変数dに代入しています。ソースにd = (short)( b * c );のように型キャストしている理由は、short型の変数どうしの掛け算の結果は、short型より情報量の多い整数の値(int型,long型など)になるため、強制的にshort型に型変換する必要があるためです。(short)を省略した場合には、計算精度が悪くなるという理由でコンパイルエラーになります。
b=50、c=200のときd=10000となりと理論値10000と変数値が一致しています。b=500、c=200のときd=-31072となり理論値100000と不一致となりました。これはshort型に格納できる値の範囲が-32768~32767だからです。なぜ、500と200を掛けた値100000が-31072になるかの説明はここでは省略します。
033 034 035 036 037 038 039 040
// 変数宣言 double e, f, g; e = 100.0; f = 0.0; g = e / f; System.out.println( "理論値=解なし 変数値=" + g ); System.out.println();
この例ではf=0.0なので数学的には割り算の結果は無しとなりますが、Javaの場合は変数gに∞(無限大)が代入されます。それはSyste.out.printlnで"Infinity"(数学での意味は無限大)と表示されることで確認できます。実際のプログラムでは、0での割り算(ゼロ除算)が起こらないように割る数値が0にならないようにチェックし、0であれば割り算を行わないようにします。
ゼロ除算がオーバーフローかどうかは諸説あるようですが、ここではオーバーフローとしています。
以上です。
関連コンテンツ
割り算で「割り切れる」、「割り切れない」ってどういうこと?