検索
連載

ソフトウェア技術者のためのバグ百科事典(11)「数値演算」のバグは奥が深い山浦恒央の“くみこみ”な話(132)(2/3 ページ)

ソフトウェア技術者に向けて、バグに関する基礎知識をまとめていく新シリーズ「バグ百科事典」。第11回は、意外と奥が深い、数値演算のバグを取り上げます。

Share
Tweet
LINE
Hatena
※本記事はアフィリエイトプログラムによる収益を得ています

2.2 オーバーフロー

 オーバーフローとは、変数の取り得る値の範囲を超える場合に発生します。この事象が発生すると、以下のような動作が起きます。

  • ゼロに戻る
  • 値がINFとなる
  • プログラムが停止する
  • 符号が反転する

 以下のリスト8、9をご覧ください。

unsigned char sum = 0;
sum = 255 + 1;
printf("合計 = %d", sum);
リスト8 オーバーフローの確認例(一部抜粋)
合計 = 0
リスト9 実行結果

 リスト8、9には、オーバーフローの例を示しました。unsigned char型の最大値は255です。255+1=256ですが、unsigned char型では取り扱えないため、「0」に戻ります。これが、オーバーフローです。

2.3 ゼロ除算

 ゼロ除算とは、0で割る場合に発生する事象のことです。変数を0で除算する場合は、計算結果が「NaN(Not a Number)」のようなおかしな値となり、場合によってはプログラムの動作が停止します(リスト10)。

int a = 255, b = 0, res = 0;
res = a / b;
printf("結果 = %d\n", res);
リスト10 ゼロ除算の例(一部抜粋)

 リスト10は、ゼロ除算のプログラムの例で、筆者の環境ではプログラムが停止しました。

 数学の授業でも、「0で割り算してはいけません」と教わりますが、プログラミングにおいても変わりません。

2.4 演算ルールを間違える

 バグのないプログラムを作成するには、演算ルールに従って計算する必要があります。例えば、「不等号の間違い」「優先順位」「演算の順序」は要注意です。例えば、下記の行列計算を考えます。

行列演算の例

 上記は行列の計算例です。一般的な乗算は両辺を入れ替えても同じ結果になりますが、行列には交換の法則は成り立ちません。演算ルールは作成するプログラムにも依存しますので、注意してください。

3.過去に発生したバグ

 以下の参考文献[2]によると、過去に下記のようなオーバーフローのバグがあったようです。

 IoT時代の特徴の一つとして、24時間365日の連続使用が一般的になったことが挙げられる。サーバ攻撃には直接関係しない例だが、米ボーイングの787型機は、無線LANを用いた機体整備を行う、いわば巨大IoT機器である。機体を最新の状態に維持するために、原則として機体の電源を切らない。
 しかし、787型機に搭載された電源制御ソフトウェアでは、この「電源を切らない」の想定が漏れていた。このソフトウェアでは、符号付き32ビット整数型を用いたカウンターにより、連続稼働時間を10ミリ秒ごとにカウントしていたが、想定漏れの結果、図4に示す通り、約248日で桁あふれを起こした。その結果、連続稼働時間を正しく計測できなくなった。
※[2]すべてわかるIoT大全2017(吉田琢也、2017年、日経BP)、222ページを一部引用。「図4」は書籍上の図番

 このバグは、オーバーフローの一例であり、「248日問題」としてよく知られています※1)。

※1)「変数をカウントアップし、1秒間スリープ」を無限に実行するプログラムを考えます。int型を4バイトとすると取り扱える範囲は、-2147483648から2147483647まで扱えますね。カウンタは0から始めるため、原理上、2147483647/60/60/24=2万4855日カウントできます。

 落とし穴は、スリープの秒数を変更した場合です。例えば、10msのスリープがあると考えると、2147483647/60/60/24/100=248.55日となり、248日目で不可解な動作が発生することとなります。

Copyright © ITmedia, Inc. All Rights Reserved.

ページトップに戻る