ソフトウェア技術者に向けて、バグに関する基礎知識をまとめていく新シリーズ「バグ百科事典」。第11回は、意外と奥が深い、数値演算のバグを取り上げます。
四則演算は、人間が生活する上で大事な「処理」の一つです。例えば、スーパーで300円の商品と500円の商品を購入すると、300円+500円=800円で、消費税を加算すると、800円×1.1=880円となりますね。
一昔前、四則演算は、手計算やそろばんで実行するのが一般的で、早く回答するには鍛錬が必要でした。現在は、ITの発展により、桁数の大きい計算も、電卓やプログラムを記述すれば簡単に計算できます。
コンピュータの登場で、筆算していた計算も簡単にできるようになりましたが、数値演算が原因で発生するバグもあり、事象によっては大事件となるものも少なくありません。そこで今回は、数値演算のバグを取り上げます。
数値演算は、プログラミング学習の最初に学ぶもので、簡単に見えますが、意外と奥が深いと考えます。
以下に、よくある数値演算のバグを示します。
コンピュータでの数値の表現には誤差が伴います。演算によっては目に見える誤差が発生することがあるでしょう。演算誤差自体は仕方ありませんが、誤差の伝搬によって、意図した結果とならないことがあります。下記に代表的な例を示します。
コンピュータの計算は有限ですから、例えば、無限級数などの計算ではどこかで打ち切らねばなりません。この誤差を「打ち切り誤差」と言います。例えば、テイラー展開の計算式を記述することは簡単ですが、結果を算出するには、どこかで演算を打ち切る必要あります。
「丸め誤差」とは、無限に続く数字を丸めることによる誤差です。例えば、「1/3」は0.33333……と無限に続きます。例えば、double型の「1/3」をC言語で実行した例を示します(リスト1、2)。
double res = 0.0; res = 1.0 / 3.0; printf("res = %.20f\n", res);
res = 0.33333333333333331483
リスト1は「1/3」について小数以下20桁で表示するプログラムです。理論上、「1/3」は0.3333……と永遠と続くことになりますが、リスト2のように途中で丸めることで誤差が発生します。
「情報落ち」とは、大きな値と小さな値を計算すると発生する誤差です。例えば下記のプログラムを考えます(リスト3〜5)。
float sum = 25671; sum += 0.0001; printf("sum = %f\n", sum);
25671.000100
25671.000000
リスト3は、25671+0.0001を実行するするプログラム、リスト4は意図した実行結果、リスト5は実際の実行結果です。25671+0.0001=25671.0001となるはずですが、結果は25671のままです。
「桁落ち」とは、絶対値が近い2つの減算を行った際に有効桁が少なくなるものです。例えば、以下の計算を考えてみます(リスト6、7)。
float a = 0.10110; float b = 0.10101; printf("sum = %f\n", a-b);
sum = 0.000090
実行結果はそれらしいのですが、有効桁数が5桁から1桁に失われてしまいました。この計算自体では問題ありませんが、この計算が伝搬すると意図しない結果となるかもしれません。
Copyright © ITmedia, Inc. All Rights Reserved.