コンピュータが計算を間違える理由

0.1を100回足せば、10になります。

いたって当たり前のことをいまさら、と思われた方も多いのではないでしょうか?

そこで、試しにプログラムを組んで確認してみます。

以下のようにプログラムを書いて実行してみました(C言語)。

#include 
#include 

int main(void){
	float sum;
	int i;
	sum=0;
	for(i=1;i<=100;i++){
		sum=sum+0.1;
	}
	printf("%f\n",sum);
	return 0;
}

結果は

10.000002

という結果になりました。10にはならないんですね。

これよりコンピュータは計算を間違えることがわかります。

データの記憶法

スイッチで記憶する

計算ミスを理解するために、コンピュータが数字を記憶する方法を理解する必要があります。

コンピュータはどのように数値を記憶しているかというと「複数のスイッチ」で記憶しています。例えば、6個のスイッチなら

ON,OFF,ON,ON,OFF,ON

といった感じに数値を記憶しています。

当然ですが、スイッチは有限個なので記憶に限界があります。

これが原因で計算ミスが生じます。

スイッチでは2進数が便利

スイッチの場合は、ONかOFFかなので、2進数を使うのが便利です。

2進数とは2つの文字0と1だけで数字を表す方法のことです。私たちが通常使うのは10進数で数字を10こ使います。

【例】

  • 10進数->2進数
  • 0->0
  • 1->1
  • 2->10
  • 3->11
  • 4->100
  • 5->101

0.1をどのように記憶するか

では、具体的に0.1をfloatでどのように記憶しているかを見ていきます。

とりあえず、コンピュータに0.1を2進数であらわしたものを表示するプログラムを書きます。

それが以下のプログラムです。

#include 
#include 
#include 

int main(void){
	float data;
	unsigned long buff;
	int i;
	char s[32];

	data=(float)0.1;
	memcpy(&buff,&data,4);
	for(i=31;i>=0;i--){
		if(buff %2==1){
			s[i]='1';
		}else{
			s[i]='0';
		}
		buff=buff/2;
	}
	s[32]='\0';
	printf("%s\n",s);
	return 0;
}

この結果から、0.1は2進数

00111101110011001100110011001101

となることがわかります。

では、解説を進めます。floatの場合は、32個のスイッチ(32ビット)で値を記憶します。さらに

  • 1番目のスイッチ:符号
  • 2から9番目のスイッチ:指数部分
  • 10から32番目のスイッチ:仮数部分

といった具合に記憶する部分を区切ってあります。仮に符号が正で、指数部分が5で、仮数部分が0.3なら

 0.3\times 2^{5}=9.6

となります。

【符号】

1番目のスイッチが符号で、0なら正、1なら負となります。よって、

00111101110011001100110011001101

は正であることがわかります。

【指数部分】

次に2から9までのを見ると

01111011

となっています。これは、十進数で

 \displaystyle 2^{6}+2^{5}+2^{4}+2^{3}+2^{1}+2^{0}=64+32+16+8+2+1=123

となります。

ここで、8個の数で、中間が0になるように指数部分を設定しています。つまり、-127から128までの数を表しており、127が0を意味します。これを使うと123は

123-127=-4

となることがわかります。よって、仮数かける2^{-4}となります。

【仮数部分】

最後の仮数部分

10011001100110011001101

は小数点以上の1桁目を1とする2進数なので

1.10011001100110011001101

という数になります。これを10進数に直すと

 \displaystyle 1+\frac{1}{2}+\frac{1}{2^{4}}+\frac{1}{2^{5}}+\cdots

で頑張って計算すると

1.600000024

となります。

以上まとめると

  • 符号:正
  • 指数部分:-4
  • 仮数部分:1.600000024

となります。よって、

 1.600000024\times 2^{-4}=0.100000001

というのがコンピュータが記憶している0.1ということになります。

私たちの世界は10進数、コンピュータの世界では2進数なので、計算に違いが出るんですね。

正確な計算をコンピュータにやらせる際は、データの保存方法を十分に理解する必要があります。

著者:安井 真人(やすい まさと)