材木を寸法ぴったりの長さに切り取りたいとき,どんな作業をするか考えてみましょう。先ずは目標の寸法のところに印を付けます。次に印が消えない程度に,少し長めにのこぎりで切り落とします。次にかんなをかけて端面を仕上げます。通常はこれで十分ですが,更に目標寸法へ追い込みたい場合はヤスリをかけます。少しずつ,少しずつ目標の寸法に近づけていくわけです。少々の隙間は良しとしたり,1mm単位で寸法を決めたり,より精度良く仕上げる場合には10分の1mm程度まで追い込んだり。現在の仕事に必要十分なところで寸法精度への要求を打ち切ります。要は必要な仕事をできるだけ効率よく行えばよいのです。
プログラミング言語の実数型を用いた計算も,同じような考え方を必要とします。こちらは選択した型によって要求できる精度が決まりますから,あとはプログラマが上手にその精度を利用する必要があります。必要以上に高い精度で計算することは無駄ですし,必要な精度で計算できなければ仕事になりません。これは言ってみれば妥協をするのではなく,見切りを付けると言うことです。今回は実数型のそんな側面について学びます。
浮動小数点数で発生するエラーや誤差
浮動小数点数の仕組み上避けられないエラーや誤差には次に挙げるものがあります。
- オーバーフロー/アンダーフロー
- 桁落ち
- 丸め誤差
- 情報欠落
- 打ち切り誤差
今回は最後の「打ち切り誤差」を学習しましょう。
打ち切り誤差
円周率や自然対数の底,
などの無限小数値の値を求めたい場合,コンピュータの数値表現の制約や実用上,通常ある精度で計算を打ち切らなければならなりません。このため計算結果は真の値とは異なります。こうした事情で発生する誤差を打ち切り誤差といいます。誤差を含む数値で各種の演算を重ねると,その結果大きな誤差が生じる危険があります。極端な例では,円周率の値を3とした場合と,3.1415とした場合では約5%の誤差が発生します。この5%の誤差が大したことでない場合もありますし,大変な場合もあります。プログラマはその状況に応じた精度で計算できるプログラムを作らなければなりません。
それならば,いっそのことより高い精度で計算しておけば良いのでは?と思うのは自然なことですが,高い精度で計算するためにはコンピュータの計算資源を余分に必要とします。できることなら,工夫なしに現在発揮できる精度いっぱいの計算をして,そこで打ち切るのが無駄のない選択肢だといえます。
打ち切り誤差は,これまでに学習した4つの誤差に比べると,プログラマの側の働きかけによって決定する部分があるので,少々ポジティブな誤差だと言えます。ポジティブだろうがネガティブだろうが,そこに誤差があるわけですから,計算を行う我々プログラマは誤差の発生理由と程度を理解しておきましょう。それではここで,自然対数の底e(ネイピア数という)の算出を例に取り上げてみます。ネイピア数は次のように定義されています。

この定義式でx=1として,nを無限に増加させていけば,限りなく真の値に近づきます。しかし,コンピュータを用いた数値計算では,精度は計算結果を格納する変数に依存します。何度繰り返して計算しても,値に変化が現れないようでは計算を続ける意味がありません。
仮に計算結果を格納する浮動小数点数型の変数に十分な余裕があるとしましょう。しかし計算にかかる時間が長くなるのであれば,その時間をかけても計算する意味のあるところまでで打ち切るべきでしょう。数千回,数万回と繰り返す必要のある計算の場合はコンピュータを長時間占有することになります。時は金なりと言います。その問題は,時間とお金をどんどん積み上げても計算する必要があるでしょうか? コンピュータを用いた数値計算では,合理的で妥当なところで計算を打ち切ります。計算を打ち切ったところで相応の誤差を含むことになるのです。
それでは,実際にfloat型の精度いっぱいまで計算してみましょう。
問題:ネイピア数の定義に従い,float型の最大精度で計算し,結果を出力しましょう。
ネイピア数eは次の式で定義されます。

ただし,x=1です。
(1) 定義式に忠実にソースコードを作成しましょう。
(2) 計算を実行し,何個目の項を加算したところでfloat型の精度いっぱいの値になるか確認しましょう。

