概要
本文書では、Excel 5.0 を始めとする多くのアプリケーションで問題となる数値の「誤差」について、若干技術的に解説をしています。
コンピュータの数値表現
私達はほとんどの場合 10 進数で計算を行います。しかし、コンピュータにとっては 2 進数の方が便利です。コンピュータは電気的に動作しており、その電気のオンかオフの状態を「 1 」と「 0 」として捉えるのが最も簡単だからです。この最小単
位を「ビット」と呼びます。1 ビットでは 2 種類、4 ビットでは 16 種類の表現が可能です。このビットを増やすことで、扱える表現数は増えることになります。
コンピュータのメモリは無限ではありません。円周率や循環小数などを完全に表すことは不可能です。そこである一定の桁でまるめてしまうことが必要になります。メモリは (ハードディスクなどに比べると) 比較的高価なデバイスですので、できるだけ少ないメモリでできるだけ大きな数字や小さな数字を表現するという相矛盾する要求を満たさなければなりません。また、1 つの数値を表すのに少ないメモリであれば計算速度も向上します。これらのことを踏まえて、いくつもの数値データの格納形式が発明されました。当初コンピュータは科学技術計算に用いられることを目的として作られましたので、精度よりも、計算速度や扱える数値範囲の大きさの方が優先されていました。その中で生まれたのが今回ここで紹介する IEEE の浮動小数点フォーマットです。
Intel 系 CPU を使ったパーソナル コンピュータ
Intel 系の CPU (80386、80486、Pentium など) および数値演算プロセッサは IEEE(後述) で定められた数値のフォーマットに則ってデザインされており、このフォーマットでの演算コマンドをハードウェア レベルでサポートしています。このためこの CPU を使ったオペレーティング システムおよびその上で動作するアプリケーションは、自動的にこのフォーマットに従ったものになります。これ以外のフォーマットを使用したときには、ソフトウェアで演算の総てを行わなければなりませんので、速度的には大きなペナルティとなります。Intel 系 CPU でサポートされている数値フォーマットおよびその派生は以下の通りです。
整数 (Integer)
長整数 (Long Integer)
単精度実数 (Single)
倍精度実数 (Double)
長倍精度実数 (Long Double)
これらの数値フォーマットを使う限り、ハードウェアの助けを借りることができます。
ただし、Microsoft(R) Windows(R) (以下 Windows) の環境で実数については浮動小数点演算プロセッサ (ユニット) が無いときにはソフトウェアでエミュレートします。例えば、i80386 CPU は標準でこのユニットを持っていません。別途専用のi80387 コプロセッサが用意されており、これと組み合わせることで実数の演算をハードウェアで行うことができます。これに対し、i80486DX や Pentium プロセッサなどは、その内部に浮動小数点演算ユニットを内蔵しています。
IEEE の浮動小数点フォーマット
IEEE 形式は、米国電気電子技術者協会 (Institute of Electrical and Electronics Engineers) により、おもにパーソナル コンピュータで使用される浮動小数点の形式を統一するために定められました。808x6 を含む 16/32 ビット CPU に対応するコンパイラなどがこの規格に統一されており、80x87 などの数値演算コ・プロセッサとも相性のよい標準的なデータ形式です。
単精度 倍精度
指数部ビット数 8 11
符号ビット数 1 1
仮数部ビット数 23 52
有効桁数 7 15
これに対し、速度や扱える数値の範囲より、精度を重要視するフォーマットも存在します。オフィスコンピュータの世界でよく用いられるのが 4 ビットで 1 桁の数値を表現する形式や小数点以下の桁を固定する、固定小数点形式で、これらは扱える数値の範囲は狭くなりますが、正確に数値を表現できます (メーカーによってフォーマットの細部は異なります) 。
浮動小数点形式への格納による誤差
浮動小数点数は小数点の位置を一定とせず、有効数字を表現する仮数部と小数点の位置を示す指数部を用いて表します。指数部は数値の大きさの範囲を定める部分で、2 のべき乗を 2 進数で表現します。負の値の指数は、相対的なバイアス値を加算することにより表現します。仮数部は数値の精度を定める部分で、この値に指数部の計算を施して元の値を表現します。この仮数部の大きさが 4 バイト長の浮動小数点数を「単精度」、8 バイト長の浮動小数点数を「倍精度」といいます。まず、この段階で小数点以下の数値は精度が落ちます。多くの小数点以下の数値は上記の方法では完全な精度での 2 進数化ができません。
また浮動小数点演算において、許容範囲を越える負の指数が生じたときは桁落ち(アンダーフロー) が発生します。また、仮数部の有効桁数を越えた数値を表現するとき、超越分をなくさないため値を左右にシフトし、それに伴って指数部の増減を行います。この操作を正規化といいますが、正規化が行われると誤差が生じ精度が落ちます。このため実数の場合には循環小数となって完全に表現しきれないことがあります。この誤差は計算上問題となりますので、保証できる精度の範囲をもって、「有効桁数」という言葉で表します。
Excel 5.0 の場合には 15 桁が有効桁数です。つまり 15 桁以上のところには誤差が含まれている可能性があるということです。しかし、この誤差を含んだ数値を演算した結果、14 桁以内に誤差が現れることもあります。
Excel 5.0 上での数値データの扱い
Excel 5.0 では、数値は 2 種類のフォーマットで扱われます。1 つは整数で、もう1 つは実数です。整数は -32,768 から +32,767 までの小数点を含まない数値で、それ以外の数値はすべて IEEE 形式の倍精度実数扱いです。いったんセルに入力された整数の範囲を越える数値は IEEE 形式の浮動小数点フォーマットに変換されてメモリに格納されます。
そして瞬時に、指定された書式で変換して画面に表示します。したがいまして、入力された数値がそのままセルに表示されているとは言っても、有効桁数の範囲外の部分もしくは表示形式によって表現される範囲外の部分に誤差を含んでいる可能性があることになります。
ここで表示される桁より下の桁をどう処理するかが問題となります。この処理を「まるめ」と呼んでいますが、これにも IEEE で幾つかのルールが決められています。パーソナル コンピュータの世界では、「最近偶数値 の 2 の数値まるめ」という方法を取っています。この処理は Excel 5.0 というよりもハードウェアもしくは Windows が行っています。さらに詳しいことがお知りになりたいときには、IEEE の規格書もしくは Intel 社の 80x87 の仕様書を参照ください。
Excel 5.0 上での計算方法
既定値では、Excel 5.0 は表示されている値ではなく、セルに保管されている値で行っています。すなわち実数では演算誤差を含んでいる可能性があります。また、整数同士の演算で、結果も整数の範囲で収まるものは誤差は伴いません。しかし、それ以外の場合には一端実数にキャスト (型変換) された後、演算が行われます。こ
のときにも演算誤差を含むことがあります。ほとんどの場合、これらの誤差は有効桁数外で発生しますので問題になることは、そう多くはありません。ただし、有効桁数の範囲内では同じ数値であっても、範囲外の値が異なっているときには IF 関数などによる比較で問題となるケースも発生します。これらのときには ROUND 系の関数でまるめを行ったあとに比較をすることで回避できます。
表示されている値 (桁数) で計算させたいときには、[ツール] - [オプション] コマンドの [計算方法] パネルで、[表示桁数で計算] チェックボックスをオンにすることで行えます。この設定は、[オプション] コマンドで設定することからも判るとおり、Excel 5.0 全体に影響を与えます。また表示形式によって表示桁数を指定しているときには、正確さも失われる可能性がありますので、設定にあたっては十分な注意が必要です。
参考: VBA での通貨型
Microsoft(R) Visual Basic(R)Programming System Applications Edition 内部には通貨型というデータ型を持っています。これは浮動小数点形式のフォーマットでは無く、固定小数点形式の独自のフォーマットです。浮動小数点と異なり、小数点の位置を固定することで扱える数値の範囲は狭くなりますが、誤差を含ませないという利点があります。このため、金額を扱う計算のために使われるデータ型として定義されています。
この型の有効桁範囲は整数部 15 桁+小数点以下 4 桁です。これは Microsoft(R) Visual Basic(R) for Windows(R) / MS-DOS(R)、Microsoft(R) BASIC Version 7.0でサポートされているものと同じです。
サンプル
Sub Sample()
'計算に使用する変数を通貨型で定義します
Dim Kingaku As Currency, Zeiritsu As Currency
'計算に使用する値を変数に代入します
Kingaku = 1995
Zeiritsu = 1.03
'計算結果を整数にして値を求めます
Kingaku = Int(Kingaku * Zeiritsu)
MsgBox Kingaku
End Sub
通貨型のデータを定義して計算に使用する場合は、その計算式で使用するデータのすべてを通貨型に定義した変数に代入して計算を行なうようにしてください。
計算結果を整数化する必要がある場合には、目的の処理に応じて Int、Fix などのVBA 関数を使用するか、Round、RoundDown、RoundUp などのワークシート関数を使用して調整するようにしてください (サンプルでは、Int 関数を用いて計算結果を整数化しています)。
各関数の機能および詳細は、オンライン ヘルプを参照してください。