• ベストアンサー

計算の誤差

a = 0; for(z=0;z<100;z++){ a += 0.04; printf("%f\n",a); } という簡単なプログラムでaを0.04ずつ大きくしていきたいのですが、これを実行すると、 …… 1.680000 1.720000 1.760000 1.799999 1.839999 1.879999 1.919999 …… といった風にわずかな誤差が生じてしまいます。 この誤差をなくす方法は無いでしょうか。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.1

本質的にはありません。 が、 ・int ではこの誤差は発生しない ・少数部分を含まない浮動小数点ではこの誤差は発生しない という事が言えませすので、 double a = 0; int ia = 0; for(int z = 0; z < 100; z++) { ia += 4; a = ia / 100.0; } というようなことで回避はできます。 おまけ。 0.4 だとこういうことがおきますが、たとえば、0.03125 だとこの誤差は発生しません。 a = 0; for(z=0;z<100;z++){ a += 0.03125; printf("%f\n",a); } これは、0.03125 = 1/2/2/2/2/2 と、/2 だけで表現できるからです。

sasmostmilk
質問者

お礼

ありがとうございました。

その他の回答 (2)

  • nda23
  • ベストアンサー率54% (777/1415)
回答No.3

2進実数を10進実数に変換すると、2のべき乗の 和で表わされる値以外は必ず誤差がでます。 10進数の何桁まで信用できるかは単精度で6、 倍精度で16桁とされています。従って、金利の 計算などでは最初から有効桁と丸め方を決めて います。これ以上の精度が必要な場合は自力で エミュレータを作るなどの処置が必要です。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

ISO/IEC TR 24732 をサポートしている C の処理系なら _Decimalなんとか を使う. ISO/IEC TR 24733 をサポートしている C++ の処理系なら std::decimal::decimalなんとか を使う. どちらでもなかったらあきらめる.

関連するQ&A

  • 計算に誤差が出る?

    0.1 + 0.2 + 0.3・・・・・・ このように行うプログラムを2通りに分けて処理をして見ました。 以下にソースを載せます。 #include <stdio.h> int main() { float sum, i; float sum2; int f; for(i=0.1, sum=0.0; i<=100.0; sum+=(float)i, i+=0.1); printf("%f\n", sum); for(f=1, sum2=0.0; f<=1000; sum2+=(float)f/10.0, f++); printf("%f\n", sum2); return 0; } 初めのfor文と2番目のfor文では同じ処理を行っているのですが、計算結果が微妙に異なって出力されてしまいました。 理由が分かる方は教えてもらえないでしょうか?

  • C++の打切り誤差についてお聞きしたいのですが・・

    ↓のプログラムが、なぜ実行結果の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 続行するには何かキーを押してください . . .

  • clock()関数の誤差

    プログラムの実行時間の計測について質問させていただきます。 現在,実行時間の計測でclock()関数を使っているのですが誤差が出ます。 timeコマンド(と実際に時計で測った時間)では95分、clock()関数で測った プログラム全体の実行時間は1376秒(約23分)と誤差が出る状態にあります。 プログラムでclock()関数を使っているのはmain()だけです。 printf()内がおかしいのでしょうか? 詳しい方、回答よろしくおねがいします。 ↓プログラム #include<time.h> (中略) clock_t t1,t2,t3,t4; (中略) main() { struct zahyo P,Q; int a,b,prime,Ord,sec; scanf("%d",&a); (中略) printf("Q.y = "); scanf("%d",&Q.y); t1=clock(); Ord=OrdCal(P,a,prime); t2=clock(); printf("Ord = %d\n",Ord); printf("OrdCal:%f(s)\n",(double)(t2-t1)/CLOCKS_PER_SEC); t3=clock(); PohlingBsgs(P,Q,a,prime,Ord); sec=secretkey(); t4=clock(); printf("secretkey=%d\n",sec); printf("Decipher:%f(s)\n",(double)(t4-t3)/CLOCKS_PER_SEC); printf("Total :%f(s)\n",(double)(t4-t1)/CLOCKS_PER_SEC); } 実行結果 Ordcal:74.170000(s) Decipher:1302.722704(s) Total :1376.902104(s) real 94m33.445s user 94m30.900s sys 0m0.980s

  • 誤差を出さずに整数計算をしたいです

    いつもお世話になっております。 0~18446744073709551615までの整数値を計算して出力するプログラムを作成しております。 しかし、計算に誤差が出てしまい意図した出力結果が得られません。 試してみた方法と出力は以下のとおりです。 ---------------------------------------------------------------------- [方法1] /* 18446744073709551615に0.0~1.0までの乗算 */ #define RANGE_MAX 18446744073709551615ULL #define MAGNIFICATION 1.0 /* 0.0から1.0 */ printf("%0.lf", RANGE_MAX * MAGNIFICATION); [出力1] 0~18446744073709552000 ---------------------------------------------------------------------- [方法2] /* 562949953421311に0.0~1.0までの乗算した結果と 562949953421312に0.0~1.0までの乗算した結果を 32767回加算した結果を加算する */ #define RANGE_MAX 562949953421312ULL #define MAGNIFICATION 1.0 /* 0.0から1.0 */ #define CYCLE 32767 unsigned long long int y = (RANGE_MAX - 1) * MAGNIFICATION; int loop; for(loop = 0; loop < CYCLE; loop++){ y += RANGE_MAX * MAGNIFICATION; } printf("%llu \n", y); [出力2] 0~9223372036854775808 ---------------------------------------------------------------------- 方法1は丸め誤差が原因だと考え方法2を試してみました。 しかし、方法2では情報落ちになってしまうのか、途中から計算されない 模様です。 誤差を出さずに計算によって0~18446744073709551615までの整数を 得るにはどのようにすれば良いのかご教授願います。

  • Perl:計算誤差について

    お世話になります。 Perlを使って、計算機には誤差があるという話をしようと思って、  print "1/3*3=", 1/3*3, "\n"; というプログラムを実行させました。 自分としては、1/3の時点で0.333333333333的な数になるので、それを3倍すると、0.9999999999的な数字になると思いましたが、結果としては1になります。 これはどういう原理でしょうか。 よろしくお願いします。

    • ベストアンサー
    • Perl
  • 浮動小数点の誤差のあわせ方

    文字列からdouble型変換で他のPGと誤差がでてしまうのですが、 なんとか同じBinaryにしたいので教えてください。 1.489を他のPGのHEXで表した結果では、 printf("1.489 = %08lx%08lx\n", atof((double)???)); >3ff7d2f1a9fbe76c 私の作ったPGでは printf("1.489 = %08lx%08lx\n", atof("1.489")); >3ff7d2f1a9fbe76d の結果になります。 丸めれば、同じになるような気がしますが、 その方法がわかりません。 SPARC でCのライブラリかなにかあるでしょうか? どうかよろしくおねがいします。

  • C言語で信頼できる時間誤差

    C言語で信頼できる時間誤差 C言語のQueryPerformanceCounterを用いて時間を計測するプログラムを作りました. このとき時間の間隔は必ずしも500m秒になりません. 原因としては, 1. Sleep関数に含まれる誤差 2. QueryPerformanceCounter関数に含まれる誤差 の2つが考えられると思っています. そこで,質問ですが,1および2の誤差ってどの程度なのでしょうか? また,その他に誤差が出現するとすれば,それは何でしょうか. ちなみに,PCはwindows XPでMicrosoft Viusal Stadio 2010で動作させ, その他のアプリケーションは開いていない状況下で実行しました. サンプルプログラムにより実行しましたが,何が原因なのでしょうか? **********【サンプルプログラム】********** #include <stdio.h> #include <windows.h> void main (void){ LARGE_INTEGER m_pCounterFreq; LARGE_INTEGER m_pBeforeCounter; LARGE_INTEGER ulCounter; double dPassageSec; double pre =0.0; int counter; printf("取得時間,取得時間間隔\n"); QueryPerformanceFrequency( &m_pCounterFreq ); QueryPerformanceCounter( &m_pBeforeCounter ); for(counter=0;;counter++){ QueryPerformanceCounter( &ulCounter ); dPassageSec = (double)(ulCounter.QuadPart - m_pBeforeCounter.QuadPart) / (double)m_pCounterFreq.QuadPart; if(dPassageSec >= 5.2) break; Sleep(500); printf("%-12f,%-12f\n", dPassageSec, dPassageSec-pre); pre = dPassageSec; } } **********【実行結果一例】********** 取得時間,取得時間間隔 0.000002 ,0.000002 0.486344 ,0.486342←14msも誤差がある 0.986095 ,0.499751 1.486106 ,0.500012 1.986102 ,0.499995 2.486100 ,0.499998 2.986116 ,0.500016 3.486089 ,0.499973 3.986090 ,0.500001←ほぼ500ms 4.486092 ,0.500002 4.986208 ,0.500117

  • 矩形法プログラムの計算値と計算誤差の求め方

    下記の矩形法の計算値を求めるプログラムで、区間分割数nを10から100まで10ずつ増やして計算値を求めるプログラムを作成したいのですが、どのようにしたらいいのか分かりません。Java初心者なのでJavaプログラムが詳しい人は至急ご連絡をお願いします。 *ちなみにこの矩形法プログラムのの計算誤差も分かる人はお願いしたいのですが・・・・・・  計算誤差の求め方は    計算誤差=(計算値ー真値)/真値×100(%) (真値=0.68269とする) public class Kukei { static double f(double x) { // ここに任意の被積分関数を記述 double y = Math.exp(- x * x / 2) / Math.sqrt(2.0 * Math.PI); return y; } public static void main(String[] args) { double a = - 1.0, b = 1.0; // 積分範囲 int n = 100; // 区間分割数 double h = (b - a) / (double)n; // 分割幅 double s = 0.0; for (int i=0; i < n; i++) { s += f(a + i * h); } s *= h; System.out.println("区間分割数 =" + n); System.out.println("矩形法による計算値 =" + s); } }

    • ベストアンサー
    • Java
  • 計算誤差について

    こんばんは。去年までフォートランをやっていたため現在C言語に苦戦中です。以下のプログラムを書いたのですが計算誤差が出てしまいます。修正をお願いいたします。 #include <iostream.h> #include <math.h> int main (void) { double t,x,pi=4.0*atan(1.0); for(t=0.0; t<=20.0; t+=0.1){ x=double(sin(pi/3*t)); cout<<t<<" "<<x<<endl; } return 0; } この文はtの値を0.1ずつを変化させて正弦波を描こうと思って書いたプログラムです。t=3.0のときsin(180°)となりx=0.0になるのはずなんですが、そうはならず10の-15乗くらいの誤差が出てしまいます。どうしたら誤差をなくせれるでしょうか? また、フォートランではt=0.1d0というような表現をして計算誤差をなくしていたのですがC(++)にはこういった方法はないのでしょうか? ご教授よろしくお願いいたします。

  • 文字の探索について

    文字列中の英大文字,英小文字の出現回数をカウントし、その回数を出力するプログラムを作りたいのですが、どうもうまく動いてくれません。 ABC123Abcのときの出力結果は 'A' = 2 'B' = 1 'C' = 1 'D' = 0 'E' = 0 中略 'X' = 0 'Y' = 0 'Z' = 0 'a' = 0 'b' = 1 'c' = 1 中略 'y' = 0 'z' = 0 のように出したいです。 私が考えた方法では まず文字列を配列str[100]に読み込み for文で、'\0'が来るまで配列の中身の文字を一つずつ判定('A'が見つかると、変数Aの値をを1つ増やす)していき、 全ての文字の判定後、 printf("'A' = %d\n", A); printf("'B' = %d\n", B); 中略 printf("'z' = %d\n", z); という風に表示しようとしたのですが、うまく表示されず、プログラム自体もとても長くなってしまい、これが正解とはいえない状態です。 こんな方法よりもっといい方法があれば教えていただきたいです。 あと、判定方法もお願いします・・・。

専門家に質問してみよう