計算途中の桁あふれ

最近はHDD容量やメモリ量も大きくなって、32bitで不足する値というものが増えてきました。
32bitで表せるのは2^32(2の32乗)=4294967296(4G)です。
そのため、例えば4G以上のファイルサイズは、32bitではbyte単位で表しきれません。
実際、多くのシステムでは、ファイルシステムやファイルにアクセスする関数で使用する型が、符号付型(signed long)であるために、2G以上のファイルを扱う際にはいろいろと問題が発生します。
この件については、また別で扱うとして、今回は桁あふれのお話です。

先ほどのファイルの話では、2GB以下の場合32bitでも問題ないと書きました。そのため、2GB以下のファイルを扱うプログラムの場合は、この32bit境界を意識しなくても良いかというとそういうわけにはいきません。

例えば、次の例があったとします。

int
calc_ratio(int size1, int size2)
{
assert(size1 < 2 * 1024 * 1024 * 1024);
assert(size2 < 2 * 1024 * 1024 * 1024);

return size1 * 100 / size2;
}

size1のsize2に対する割合(%:百分率)を計算する関数です。
size1, size2とも実用上2G以下しか使わないというassertを入れているので、このシステムではsizeには2G以下しか指定されないのでしょう。

よって、この式は問題ないのでしょうか?

実際は、問題があります。
それは、size1が2G近い値の場合です。
その場合、size1 * 100は32bitでは表しきれない値となってしまい、桁あふれを起こします。その桁あふれを起こした計算結果をsize2で割るので、その結果も不正なものになってしまいます。
対策としては、size1や定数100を64bitでキャストするなどして、計算途中での桁あふれを防ぐことです。

非常に初歩的なバグですが、このようなバグの問題は、意外に後になって発見されることが多いように思います。なぜなら、size1が大きな値にならない限り問題が表面化しないからです。
境界値テストを重要性を物語ってもいます。

コメント

非公開コメント

本のおすすめ

4274065979

4844337858

482228493X

4904807057

4873114799


プロフィール

  • Author:proger
  • 組み込み関係で仕事してます

ブログ内検索