サンプル・プログラム
overflow.php | サンプル・プログラム本体。 |
pahooInputData.php | データ入力に関わる関数群。 使い方は「PHPでGET/POSTでフォームから値を受け取る」「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。 |
integer 型の制約
52: /**
53: * integer型のオーバーフローを見る
54: * @param なし
55: * @return なし
56: */
57: function overflow1() {
58: echo '■integer型のオーバーフロー/アンダーフローを見る<br />';
59: echo 'PHP_INT_SIZE = ' . PHP_INT_SIZE . '<br />'; //integer型のバイト長
60: echo 'PHP_INT_MAX = ' . PHP_INT_MAX . '<br />'; //integer型の最大値
61: echo '<hr />';
62: //正数のオーバーフロー
63: $a = PHP_INT_MAX - 2;
64: for ($i = 0; $i < 5; $i++) {
65: var_dump($a);
66: echo '<br />';
67: $a++;
68: }
69: echo '<hr />';
70: //負数のアンダーフロー
71: $a = 0 - PHP_INT_MAX;
72: for ($i = 0; $i > -5; $i--) {
73: var_dump($a);
74: echo '<br />';
75: $a--;
76: }
77: }
実際には32ビット環境と64ビット環境で変わり、以下のようになっている。
ビット長 | PHP_INT_SIZE | PHP_INT_MAX | |
32ビット環境 | 32ビット | 4 | 2,147,483,647 |
64ビット環境 | 64ビット | 8 | 9,223,372,036,854,775,807 |
■32ビット環境
PHP_INT_SIZE = 4
PHP_INT_MAX = 2,147,483,647
-231 ≦ integer ≦ 231 - 1
-2,147,483,648 ≦ integer ≦ 2,147,483,647
■64ビット環境
PHP_INT_SIZE = 8
PHP_INT_MAX = 9,223,372,036,854,775,807
-263 ≦ integer ≦ 263 - 1
-9,223,372,036,854,775,808 ≦ integer ≦ 9,223,372,036,854,775,807
プログラムを実行すると、この制約を上回る/下回ると、integer 型から float 型へ自動的に移行するのが分かる。
float型から integer型へは自動移行しない
79: /**
80: * float型からinteger型へは自動移行しない
81: * @param なし
82: * @return なし
83: */
84: function overflow2() {
85: echo '■float型からinteger型へは自動移行しない<br />';
86: //正数
87: $a = PHP_INT_MAX + 2;
88: for ($i = 5; $i > 0; $i--) {
89: var_dump($a);
90: echo '<br />';
91: $a--;
92: }
93: echo '<hr />';
94: //負数
95: $a = -3 - PHP_INT_MAX;
96: for ($i = -5; $i < 0; $i++) {
97: var_dump($a);
98: echo '<br />';
99: $a++;
100: }
101: }
もし integer 型にしたいのであれば、強制的にキャストしてやる必要がある。
float 型の制約と誤差
103: /**
104: * float型のオーバーフローを見る
105: * @param なし
106: * @return なし
107: */
108: function overflow3() {
109: echo '■float型のオーバーフロー/アンダーフローを見る<br />';
110: //float型のオーバーフロー(大数側)
111: $a = +1.79E+305;
112: for ($i = 0; $i < 5; $i++) {
113: var_dump($a);
114: echo '<br />';
115: $a *= 10;
116: }
117: echo '<hr />';
118: //float型のオーバーフロー(小数側)
119: $a = +4.94E-323;
120: for ($i = 0; $i < 5; $i++) {
121: var_dump($a);
122: echo '<br />';
123: $a /= 10;
124: }
125: echo '<hr />';
126: //float型のアンダーフロー(大数側)
127: $a = -1.79E+305;
128: for ($i = 0; $i < 5; $i++) {
129: var_dump($a);
130: echo '<br />';
131: $a *= 10;
132: }
133: echo '<hr />';
134: //float型のアンダーフロー(小数側)
135: $a = -4.94E-323;
136: for ($i = 0; $i < 5; $i++) {
137: var_dump($a);
138: echo '<br />';
139: $a /= 10;
140: }
141: }
つまり、絶対値の範囲は下記の通りである。
4.9406564584124654E-324 ≦ float ≦ 1.7976931348623157E+308
これをプログラムで確かめると、この範囲をオーバーフローすると 'INF' または '0' になる、逆にアンダーフローすると '-INF' または '-0' になることが分かる。INFは無限大を意味する。
また、10で除算しているにもかかわらず、2進数特有の“誤差”が発生していることも分かる。
これほどの大きさ/小ささの数値データを扱うことは希だろうが、もしループ処理内でオーバーフロー/アンダーフローしてしまうとINFや0が返るので、PHP処理系ではエラーを出さずに無限ループに陥る恐れがある。注意しておきたいところだ。
制約を超えると演算が正常に行われなくなり、プログラム全体の動作がおかしくなる場合がある。最悪の場合、プログラムが無限ループに陥ったり、思わぬエラーを晒したりする恐れがあるので、セキュリティ上、注意が必要だ。