プログラマーを悩ませるもののひとつは、丸め誤差です。
そして、もうひとつ挙げるならば、「桁あふれ(オーバーフロー)」でしょう。
なぜ悩ませるのか?
なにかを計算する時には、本来は必ず、これらを意識する必要があるからです。
最近では、CPUの性能も上がっているため、一度に取り扱える数値も大きくなっています。よって、あまり、これらを意識することがない場面も多くなりました。
しかし、これらは、とても重要な考え方なのです。ここでそれを学んでください。
桁あふれ(オーバーフロー)とは何か?
まず、丸め誤差については、丸め誤差とは何か?小数点以下の数値をどう扱うかを知る!で書いていますので、こちらを確認してみてください。
それでは、桁あふれとは何か?
それは、「扱える数値の許容範囲を超えた」ということなのです。
次に、その例を見ていきます。
桁あふれ(オーバーフロー)はどうのように起こるのか?
C言語の例として、2バイトの変数で考えてみましょう。
符号なし(unsigned short)と符号あり(short)変数に、それぞれ許容範囲の最大値とそれに1を加算した結果を確認する単純なプログラム(test1.c)を示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
/* test1.c */ #include <stdio.h> int main() { unsigned short us1; /* 符号なし2バイトデータ */ short s1; /* 符号あり2バイトデータ */ /* -------------------------------*/ /* -- 符号なしの例 -- */ /* -------------------------------*/ /* 最大値65535を代入し表示 */ us1 = 65535; printf("符号なし65535 ⇒ %u\n",us1); /* 1を加算し、表示 */ us1 ++; printf("符号なし65536 ⇒ %u\n",us1); /* -------------------------------*/ /* -------------------------------*/ /* -- 符号ありの例 -- */ /* -------------------------------*/ /* 最大値32767を代入し表示 */ s1 = 32767; printf("符号あり32767 ⇒ %d\n",s1); /* 1を加算し、表示 */ s1 ++; printf("符号あり32768 ⇒ %d\n",s1); /* -------------------------------*/ return 0; } |
それでは、これを実行した結果ですが、以下のようになります。実行モジュールは、「test1」です。
1 2 3 4 5 6 |
$ ./test1 符号なし65535 ⇒ 65535 ・・・・(1) 符号なし65536 ⇒ 0 ・・・・(2) 符号あり32767 ⇒ 32767 ・・・・(3) 符号あり32768 ⇒ -32768 ・・・・(4) $ |
いかがでしょうか?
C言語では、標準出力(画面表示)を行う書式指定ができます。
符号なしの整数表示が「%u」、符号ありの整数表示が「%d」です。
(1)および(3)は、許容範囲の最大値の表示ですので、正しく表示されています。
しかし、(2)と(4)は、どこかおかしいです。
これが、桁あふれ、オーバーフローというものなのです。
まず、(2)ですが、65536を2進数で表記すると、
1 0000 0000 0000 0000(B)となり、17bitで表記されます。
1111 1111 1111 1111
+
1
で、1 0000 0000 0000 0000となり、
桁があふれているのです。
そして、変数は、2バイトですので、1bit目は無視され、残りの16bitの「0」が表示されているのです。
次に、(4)ですが、32768を2進数で表記すると、
1000 0000 0000 0000(B)となり、符号ビットである1ビット目にマイナスを表す「1」が立っています。
つまり、2bit目~16bit目までの15bitまでが許容範囲であるにもかかわらず、1bit目も必要となり、16bitで表記する必要があるわけです。
ですので、この場合も、桁が符号ビットにまで溢れているということになり、桁あふれになっていわけです。
単純に考えると、許容範囲の最大値+1は、許容範囲の最小値に戻るということになります。以下の許容範囲の数値を見ると、それを理解できると思います。
2バイトで許容される数値は、以下となります。
- 符号なしの場合:0~65535(65536通りの数値)
- 符号ありの場合:-32768~32767(65536通りの数値)
符号なしの場合:
許容範囲の最大値(65535)に1を加えた「65536」を代入すると桁あふれが発生します。
65536を2進数で表すと、
1 0000 0000 0000 0000(B) で、17bit必要となり、16bitを超えるからです。
符号ありの場合:
許容範囲の最大値(32767)に1を加えた「32768」を代入すると桁あふれが発生します。
32768を2進数で表すと、
1 000 0000 0000 0000(B) で、16bit必要となり、15bitを超えるからです。
桁あふれ、オーバーフローが発生すると、計算結果や条件文などに影響を及ぼします。特に、許容範囲を超えるか否かを設計の段階で確認し、超えるようであれば、さらに変数の型を拡張する必要があります。
整数型では、4バイト変数である「long型」、実数では、8バイト変数である「double型」を推奨します。
フラグなどで大きな値を扱わないようなケースでは、上記例のように2バイト変数を使用しても問題はありません。いずれにしても、変数の型をよく検討することが重要です。
カテゴリ:プログラミング