コンピュータでの実数の計算

 コンピュータで扱われる数は基本的に2種類です。1つは整数型と言われるもの,もう1つは実数型と言われるものです。

 整数型の数については,「コンピュータでの整数の計算」で説明しました。ここでは,Tiny Basic で実際に扱う数である実数型の数について説明します。

 コンピュータの実際の計算で使われる数は,整数だけでなく,小数や分数を使う必要もあります。 そして整数以外を含む計算は整数型ではなく,実数型といわれる数で普通計算されます。この実数型の計算をコンピュータの内部ではどのように行っているのか,簡単に説明しましょう。

分数の取り扱い

  普通のコンピュータソフトでは分数は小数に直してから扱い,分数をそのまま扱うことはありません。 勿論,数式処理ソフトと言う種類のソフトでは,分数を分数のままで計算するものもあります。しかし,この方法は意外と大変で,そのような方法が本当に必要な場合にのみ使われ,普通は分数を小数に直して計算を行います。

 しかし,このことから,分数に関係する計算は,殆んど必ず誤差が生じるということになります。実際,何進法で小数を表すにしても,多くの分数は無限小数になります。実際の小数計算は無限小数の計算ではなく,適当な部分切り捨てた(或いは四捨五入した)有限小数で計算しますから,ここで誤差が生じます。ですから,

 コンピュータでの分数を使った計算結果には,

ほぼ必ず誤差が含まれる

となります。このことはシステム上,仕方の無いことですが,コンピュータで計算をする場合忘れてはならないことです。

固定小数点数

 コンピュータで小数を扱う場合,その数を格納する仕方が2種類あります。

 まず,整数型数の方式に小数点に位置を考えるという固定小数点数があります。

 例えば,8ビットで半分を小数部分にするとします。これは

       

       

の8個の箱を左右半分にして,その間に点・を入れ小数点と考えます。この方法で例えば符号なし小数を表すとすると,最小の数0が

0 0 0 0

0 0 0 0

と格納され,最大の数255/16=15.9375が

1 1 1 1

1 1 1 1

と格納されます。この方法は整数の場合と全く原理は同じで,ただ小数点をつけ,加減乗除の計算も小数点の位置の扱いに注意すればほぼ同様に行うことが出来ます。

 この固定小数点数は,扱う数の範囲が固定されている場合は便利ですが,汎用の計算の場合,少し問題があります。それは大きな数や小さな数を同時に扱うことに無理があるからです。

 例えば,上の例の場合,最小数は0で最小の刻みは1/16=0.0625です。ですから,0.01を表すことができません。また,最大数は15.9375でそれ以上の数は表すことができません。勿論大きなメモリーを使えば,どんなに小さな数も表すことが出来ますし,どんな大きな数を表すこともできます。しかし,その両方を満たすとすると,非常に大きなメモリーを必要とします。

 このようなことに対する対処法として,物理学等で古くから行われてきた記法があります。それは指数形式と言われる記法ですが,それを利用したのが浮動小数点数です。

浮動小数点数

 物理学や化学では大きな数と小さな数の両方を扱います。例えば,

水素原子の質量 = 1.6×10-24  

1光年=9.46×1015  m

のように表します。この方法を参考にして,コンピュータでは,小数を

符号 仮数 × 2指数-q

の形に表して,メモリーに(符号,仮数,指数)または(符号,指数,仮数)を格納する方法を使います。この方法で表された数を浮動小数点数といいます。qを下駄と言います。

 ここで,仮数や指数をどのように表現したりするかはいくつかの方法がありますが,このような形式で表された数を実数型数と言います。つまり,

浮動小数点数として表される数を実数型

といいます。実数型の数は上の様な形式のものの総称ですので,実際に使われるのものは色々あり,多少異なりますが,基本的性質は同じです。

 簡単な例として,8ビットで浮動小数点を考えてましょう。

s     e   f  

sは符号を表す1ビット,eは指数を表す4ビット,fは仮数を表す3ビットとします。下駄として,q=9とします。そして仮数fは小数点以下の数を表すとし,実際の数は符号 1.f ×2e-9とします。ここで,1.f は2進数としてfの表現を小数点以下のものと見て,1位に1を立てたものとします。また,e=0 の場合は値は0とします。

 この場合の0より大きい最小数はe=1,f=0 の場合で,2-8=1/256です。メモリーには

0 0 0 0 1 0 0 0

のように格納されます。また正の最大数はe=15, f=7の場合で,1.875×2=120です。メモリーには

0 1 1 1 1 1 1 1

のように格納されます。ここで,1.f は実際には,f=111(2進)ですから,1.111(2進)であり,

1+1/2+1/2+1/2=1.875

で与えられることに注意してください。

 負の数は符号ビットに1を立てるだけですから,正の場合と同じ絶対値の数を表すことができます。

 このようにな方法を用いると,8ビットでも

負の数-120から正の数120までの数

を表すことができます。しかも,

1/256

といった数も表現でき,大きな数も小さな数も表すことが出来ます。

 しかし,今の場合有効桁(=fのビット数)は2進3桁ですから,10進で表すと,実は1桁も正確でないことになります。このように,浮動小数点数は大きな数も小さな数も少ないメモリーで表すことが出来ますが,あくまでも概数であることに注意してください。

 勿論,e と f を大きく取れば,かなりの精度で数を表現することが出来ます。

Tiny Basic for Windows での浮動小数点数

 実際的なものとして, Tiny Basic for Windows で使われている実数について説明します。

Tiny Basic for Windows の実数は80ビット浮動小数点数

を使っています。

s       e                 f            

ここで,s=1ビット, e=15ビット, f=63ビット, 下駄 q=16383 で,1ビット未使用です。

0<= e <32767 に対して,(-1)×2(e -16383)×1.f

で計算されます。これで表される正数は約

  • 最小数10進で3.6×10-4951
  • 最大数10進で1.1×104932
  • 有効桁数10進19~20桁

です。普通,計算機で使われる倍精度実数は64ビットを使っていますから, Tiny Basic の実数はそれよりも高い精度を持っています。 例えば,概数ですが,

1500の階乗 = Factorial(1500) = 4.81199779677977E4114

その逆数 =1/Factorial(1500) = 2.07813893985822E-4115

といった計算が可能です。

 有効桁数も普通に使われる倍精度では,16桁ですが,Tiny Basic では内部的にはそれ以上の有効桁数を持っています。(表示は,18桁まで表示することができます。)ですから,かなりの計算を重ねても,表示桁数では正しい表示が出ます。

 また,Tiny Basic for Windows では具体的実数表現を表す関数として

FloatToStrBin と FloatToStr

があります。FloatToStrBinは内部の表現を2進的に表したもので,FloatToStrは10進的に表したものです。

Print FloatToStrBin(100)
Print FloatToStr(100)

を試してみてください。

浮動小数点数の加減乗除

  浮動小数点数の加減乗除はシステムレベルでサポートされていますから,それを実際にプログラミングすることは普通はありません。ですから,その実際を詳しく知る必要はありません。しかし,その方法の大枠を知ることは,一般のプログラムを書く人にとっても有益です。

正規化

 一般に,内部表現に適した指数表現にすることを正規化といいます。

 これは,上の方式では,数は

±1.f×2e-q  (2進表現)

と表すようにe を調整することです。例えば,11.1×2は 1.11×22と表し直すことです。また浮動小数点の表し方として,数を

±0.f×2e-q  (2進表現)

と表す方法もありますが,この場合は,11.1×2は 0.111×23と表し直すことです。

 浮動小数点数はメモリーに格納する際必ず,正規化されますから,計算は全て正規化された数から始め,正規化された数で終わることになります。

加法

2つの浮動小数点数α,β

α=1.f × 2e-q,  β=1.g × 2h-q

とするとき,その加法は次の手順で行われます。

  1. 指数部分を同じにするように仮数部分を調整する。
  2. 仮数部分の加法を行う。
  3. 結果を正規化する。

 ここで正規化の際,収まらない桁は1つ下の桁で丸められます。即ち,1つ下の桁が1の場合は上位に繰り上げ,0の場合は切り捨てします。(0捨1入)

 上の8ビットの場合の例を挙げます。仮数部は2進数で表します。

α=1.11×2, β=1.01×2

としたとき,α+βを計算します。

  1. α=1.11×2, β=0.00101×2とする。

  2. α+β=1.11101×2とする。

  3. 結果を1.111×2と正規化する。

  この計算途中で仮数部分は丸められます。4桁目が0ですから,最後の桁が切り捨てられ,計算結果は真の結果より少なくなっています。実際,α=28(10進),β=2.5(10進)ですから,真の値は30.5ですが,計算結果は30になっています。

 このように,大きな数と小さな数を加えた場合,小さな数の部分が切り捨てられてしまうことがあります。 丸めですから大きくなることもあります。

 しかし,これは,今の場合の有効桁が少ないことによるもので,仕方の無いことと言えます。

減法

 浮動小数点数の減法も加法と同様に行われます。例を挙げましょう。

α=1.11×2, β=1.101×2

としたとき,α-βを計算します。これは

  1. 指数部分を合わす。既にあっているのでそのまま。
  2. α-β=0.001×2とする。
  3. 計算結果を1×2-2 と正規化する。

となります。この結果は正しい結果を与えていますが,有効桁が1桁になっていることに注意してください。今の場合,有効桁に関わらず正しい結果になっていますが,それは,元々与えられた浮動小数点数に誤差が無いためです。


 もう少し実際的な例で考えます。

1/6 - 1/7 を計算します。この計算結果は0.02380...ですから,今の8ビット浮動小数点数での近似小数は1.1×2-6= 0.0234375です。

 他方,

1/6=0.166666...の近似小数は 1.011×2-3=0.171875 

であり(丸めで繰り上がりが起き大きくなります),

1/7=0.142857...の近似小数は 1.001×2-3=0.140625 

です。この浮動点小数に対して減法を行うと,結果は0.01×2-3=1×2-5=0.03125

となり,この浮動小数点数で表すことの出来る真の値の近似値よりも悪い値になっています。つまり

真の値の近似数=1.1×2-6

計算結果=1×2-5

となります。これはこの計算過程で,1.1となるべき有効桁の内1桁が落ちてしまったことに依ります。このように,近寄った数の差をとると,期待される有効桁が落ちてしまいます。これを桁落ちと言います。ですから,

浮動小数点数では近寄った数の差を取ると精度が落ちる

ことが多いことに注意しましょう。

乗法

 浮動小数点数での乗法は加法・減法よりむしろ単純です。次の手順で行われます。

  1. 仮数の乗法と,指数の乗法を行う
  2. 正規化をする。

と言うことになります。例を挙げます。

α=1.11×2, β=1.01×2

としたとき,α×βを計算します。

  1. 1.11×1.01=10.0011より,α×β=10.0011×2=1.00011×2となる。
  2. 正規化して(繰り上がりがあり),1.001×2を得る。

となります。α×βの真の値は17.5ですが,この近似小数は18ですから,精度の範囲で正しい値になります。このように,

乗法では,計算結果が表示可能な範囲内にあれば,

有効精度は大体保たれます。

除法

 除法も乗法と同様に次の手順で行われます。

  1. 仮数の除法と,指数の除法を行う
  2. 正規化をする。

と言うことになります。例を挙げます。

α=1.11×2, β=1.01×2

としたとき,α÷βを計算します。

  1. 1.11÷1.01=1.0110...より,α÷β=1.011×2となる。
  2. 結果は既に正規化されている。

となります。α×βの真の値は2.8で,この近似小数は2.75=1.011×2ですから,精度の範囲で正しい値になります。このように,

除法でも,計算結果が表示可能な範囲内にあれば,

有効精度は大体保たれます。

まとめ

 浮動小数点数について注意することをまとめると,

  • 分数を含む計算はほぼ必ず誤差を含む
  • 浮動小数点数は大きな数から,小さな数まで効率的に表現できる。
  • 計算は整数型より複雑で,時間が掛かる。
  • 浮動小数点数の加減乗除では,近い数の差を取る計算で誤差が大きくなる可能性が高い

となります。更に,

Tiny Basic for Windows の浮動小数点数は80ビットと,

精度が高いので普通の計算では誤差について,

特別な配慮をしなくてもかなり正確な結果が出る

ことにも注意しておきましょう。