dポイントプレゼントキャンペーン実施中!

↓のプログラムが、なぜ実行結果のsumが1ではなく1.000054や9.99999999999906e-001、9.9999999999990619e-001といった答えにになってしまうのでしょうか?

10000のところを512でやった場合は普通に1と出力されたのに・・・


#include <stdio.h>

int main(void){

int i;
float f;
double d;

f = 0.0;
for(i = 0; i<10000; i++) {
f += 1.0/10000;
}
printf("float: n=%d sum=%f\n", 10000, f);

d = 0.0;
for(i = 0; i<10000; i++) {
d += 1.0/10000;
}// end for
printf("double:16.14e: n=%d sum=%16.14e\n", 10000, d);
printf("double:18.16e: n=%d sum=%18.16e\n", 10000, d);

return 0;
}


実行結果

float: n=10000 sum=1.000054
double:16.14e: n=10000 sum=9.99999999999906e-001
double:18.16e: n=10000 sum=9.9999999999990619e-001
続行するには何かキーを押してください . . .

A 回答 (4件)

「打ち切り誤差」と言ってるので、2進⇔10進の変換を有限桁でやろうとすると、誤差が出るのは御存じなのですよね。



floatやdoubleは、内部では 「a * 2^b : ^はべき乗、 a, bは有限範囲の整数」という形で表現されています。
※ 正確にはちょっと違いますが、説明のため、等価なものに変換しています。

1/512= 1 * 2^(-9) です。これは、a=1,b=-9と内部表現との間に誤差は出ません。 誤差が無いので、512回足しても 512 * 2^-9 となり、1 * 2^0 ぴったりになります。

対して, 1/10000 を a * 2^b と表現しようとすると、 a,bともに無限の桁が必要で、有限桁では表現できません。そのため、表現できる範囲で打ち切ります。残りが誤差となります。
この誤差のある状態で加えていくので、どんどん誤差が蓄積されていきます。
それが、その実行結果です。

floatとdoubleで差があるのは、a,bの範囲の違いで、floatの方が取れる範囲が小さいため、その分誤差も大きくなります。


これは大抵のプログラム言語や計算ソフトに共通する問題です。
式を工夫して計算順番を変える(1を10000回足して10000で割ったものでは誤差は出ません)、特定の計算に特化した方法を使う(有理数の範囲で使うなら、内部で整数/整数で表現される「有理数クラス」を用意する)等、誤差対策が必要です。
    • good
    • 0

まあ、コンピューターがやることですから、


そのくらいの誤差はあるってことです。
    • good
    • 0

どうでもいいけど「打ち切り誤差」じゃなくて「丸め誤差」だよね.

    • good
    • 0

コンピュータが扱う二進数では整数を全て2のn乗の和で表現できます。


しかし小数の場合は2の-n乗の和で表現できない数値があるため記憶する数値に誤差が生じます。

つまり1/512はちょうど2の-9乗のため誤差なく記憶できます。
しかし1/10000=0.0001は2の-n乗の和で表現できないため誤差が生じます。

試しに2のn乗となる数値で割るようにすればオーバーフローを起こさない限り計算結果は1になるはずです。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


このQ&Aを見た人がよく見るQ&A