連載
» 2020年08月17日 10時00分 公開

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

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

[山浦恒央 東海大学 大学院 組込み技術研究科 非常勤講師(工学博士),MONOist]

1.はじめに

四則演算 ※写真はイメージです

 四則演算は、人間が生活する上で大事な「処理」の一つです。例えば、スーパーで300円の商品と500円の商品を購入すると、300円+500円=800円で、消費税を加算すると、800円×1.1=880円となりますね。

 一昔前、四則演算は、手計算やそろばんで実行するのが一般的で、早く回答するには鍛錬が必要でした。現在は、ITの発展により、桁数の大きい計算も、電卓やプログラムを記述すれば簡単に計算できます。

 コンピュータの登場で、筆算していた計算も簡単にできるようになりましたが、数値演算が原因で発生するバグもあり、事象によっては大事件となるものも少なくありません。そこで今回は、数値演算のバグを取り上げます。

⇒連載「山浦恒央の“くみこみ”な話」バックナンバー

2.よくある数値演算のバグ

 数値演算は、プログラミング学習の最初に学ぶもので、簡単に見えますが、意外と奥が深いと考えます。

 以下に、よくある数値演算のバグを示します。

2.1 演算誤差が発生する

 コンピュータでの数値の表現には誤差が伴います。演算によっては目に見える誤差が発生することがあるでしょう。演算誤差自体は仕方ありませんが、誤差の伝搬によって、意図した結果とならないことがあります。下記に代表的な例を示します。

2.1.1 打ち切り誤差

 コンピュータの計算は有限ですから、例えば、無限級数などの計算ではどこかで打ち切らねばなりません。この誤差を「打ち切り誤差」と言います。例えば、テイラー展開の計算式を記述することは簡単ですが、結果を算出するには、どこかで演算を打ち切る必要あります。

2.1.2 丸め誤差

 「丸め誤差」とは、無限に続く数字を丸めることによる誤差です。例えば、「1/3」は0.33333……と無限に続きます。例えば、double型の「1/3」をC言語で実行した例を示します(リスト1、2)。

double res = 0.0;
res = 1.0 / 3.0;
printf("res = %.20f\n", res);
リスト1 丸め誤差を確認するプログラム(一部抜粋)
res = 0.33333333333333331483
リスト2 実行結果

 リスト1は「1/3」について小数以下20桁で表示するプログラムです。理論上、「1/3」は0.3333……と永遠と続くことになりますが、リスト2のように途中で丸めることで誤差が発生します。

2.1.3 情報落ち

 「情報落ち」とは、大きな値と小さな値を計算すると発生する誤差です。例えば下記のプログラムを考えます(リスト3〜5)。

float sum = 25671;
sum += 0.0001;
printf("sum = %f\n", sum);
リスト3 情報落ちを確認するプログラム(一部抜粋)
25671.000100
リスト4 意図した実行結果
25671.000000
リスト5 実際の実行結果

 リスト3は、25671+0.0001を実行するするプログラム、リスト4は意図した実行結果、リスト5は実際の実行結果です。25671+0.0001=25671.0001となるはずですが、結果は25671のままです。

2.1.4 桁落ち

 「桁落ち」とは、絶対値が近い2つの減算を行った際に有効桁が少なくなるものです。例えば、以下の計算を考えてみます(リスト6、7)。

float a = 0.10110;
float b = 0.10101;
printf("sum = %f\n", a-b);
リスト6 絶対値の小さい減算の例(一部抜粋)
sum = 0.000090
リスト7 実行結果

 実行結果はそれらしいのですが、有効桁数が5桁から1桁に失われてしまいました。この計算自体では問題ありませんが、この計算が伝搬すると意図しない結果となるかもしれません。

       1|2|3 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.