コンピュータが計算を間違える理由
2013年3月22日:アルゴリズム
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なら
となります。
【符号】
1番目のスイッチが符号で、0なら正、1なら負となります。よって、
00111101110011001100110011001101
は正であることがわかります。
【指数部分】
次に2から9までのを見ると
01111011
となっています。これは、十進数で
となります。
ここで、8個の数で、中間が0になるように指数部分を設定しています。つまり、-127から128までの数を表しており、127が0を意味します。これを使うと123は
123-127=-4
となることがわかります。よって、仮数かけるとなります。
【仮数部分】
最後の仮数部分
10011001100110011001101
は小数点以上の1桁目を1とする2進数なので
1.10011001100110011001101
という数になります。これを10進数に直すと
で頑張って計算すると
1.600000024
となります。
以上まとめると
- 符号:正
- 指数部分:-4
- 仮数部分:1.600000024
となります。よって、
というのがコンピュータが記憶している0.1ということになります。
私たちの世界は10進数、コンピュータの世界では2進数なので、計算に違いが出るんですね。
正確な計算をコンピュータにやらせる際は、データの保存方法を十分に理解する必要があります。
著者:安井 真人(やすい まさと)
@yasui_masatoさんをフォロー