• ベストアンサー

マイナスを使ってはいけない理由を教えてください。

マクローリン級数展開をコンピュータで計算する際に、マイナスの値を使うとできない計算があります。具体的な式は、e=1+x+xの2乗/2の階乗+xの3乗/3の階乗+xの4乗/4の階乗....xのn乗/nの階乗。 この計算をプログラムでやると正しいeの値がでません。私の作ったプログラムを以下に載せます。 #include <stdio.h> #include <stdlib.h> #include <math.h> #define NUMBER 1.e-15 main() { double c,x,y,total,myexp(); x=-10; y = myexp(x); total=exp(x); printf(\"input=%f\\n\",x); printf(\"myexp=%.10e\\n\",y); printf(\" exp =%.10e\\n\",total); printf(\" \\n\"); } double myexp(double x) { int count=1; double total,i,ex,hiritu; total = 1.0; ex= 1.0; hiritu=1.0; // if(x < 0 ){ // x=x*-1; // i=1; // } while(fabs(hiritu) >= NUMBER){ hiritu = total/ex; printf(\"count= %d \",count); total *= x/count++; printf(\" total=%.10e \",total); ex += total; printf(\" ex =%.10e \",ex); printf(\"\\n\"); } // if(i == 1){ // ex = 1/ex; // } return(ex); } //の行をコメントではなくすると、マイナスの値を入れたときでも、正確な値を出すプログラムになります。デバックの行なども入っていますが、コンピュータで、このプログラムを用いて、マクローリン級数展開をすると、マイナスの値で正確な値がでない理由を知りたいです。ヒントでもいいのでお願いします。

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

  • ベストアンサー
  • mtaka2
  • ベストアンサー率73% (867/1179)
回答No.2

浮動小数点演算では、近い数字の引き算は精度がかなり低くなってしまいます。 以下、10進数で簡単に説明しますが、有効数字4桁の数字「1.234」と「1.222」を足す場合、結果は「2.456」で有効数字は4桁のままです。 ところが、この二つを引くと、1.234-1.222=「0.012」で、有効数字が2桁に落ちてしまっています。 そのため、引き算が入る場合は、有効数字は「○桁」ではなく「○の位」で考える必要があります。この例だと、有効数字は0.001=1e-3の位まで 今のプログラムの例だと、 count=6~count=14あたりでは、変数totalが1e+3オーダーで足し算引き算しています。 double の有効数字は10進16桁ですので、この段階での有効数字は1e+3 から16桁、1e-13 の位までです。 exは最終的に1e-5のオーダーになっていますが、 上記演算の有効数字は1e-13の位までですので最終結果の有効数字も1e-13の位まで。 すなわち、有効数字8桁になります。 というわけで、 > myexp=4.5399929699e-05 > exp =4.5399929762e-05 これも8桁までしか合わないことになってるわけです。

noname#80345
質問者

お礼

本当にありがとうございました。 今回、自分で桁落ちするプログラムをつくり、コンピュータの仕組みがわかりました。 教えてくださった方のように、私もプログラミングをしっかり勉強し、 社会の役に立てるよう学生生活を一生懸命頑張ります。 ありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (1)

  • Werner
  • ベストアンサー率53% (395/735)
回答No.1

浮動小数点数を使った演算では桁落ちや情報落ちなどの誤差が発生します。 マイナスの値の時は誤差が大きくなるような条件を満たしていたのでしょう。 http://www.cc.kyoto-su.ac.jp/~yamada/pB/float.html#error

noname#80345
質問者

お礼

ありがとうございました。 参考URLを見て、桁落ちと情報落ちの仕組みを理解することができました。 3日間わからなくて、考えていたのですが、ようやく疑問を解くことができてとてもうれしいでした。なかなかわからなくて、わかんねーと叫びながら、数字を見て調べていました。 たすかりました。 ありがとうございます。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • テイラー展開(C言語)

    こんにちは<_ _> 毎度もうしわけありません・・・ exp(x)をテイラー展開を用いて計算せよという問題で -----------------------外を与えられ サイトなどを見て見よう見まねで-----内を組んでみましたが 当然思い通りになるわけでもなく;; テイラー展開という考え方は1.00007の何乗を1と.00007分に分けて 計算するというものであるという認識です。 (そもそも言葉自体始めて見て、サイトで調べてみたもので あっているかどうかも・・・) 以下の結果 「 x myexp(x) exp(x) 0.0 1 1 10.0 110123 22026.5 20.0 4.85165e+009 4.85165e+008 30.0 1.60297e+014 1.06865e+013 40.0 4.70771e+018 2.35385e+017」 こうなりましたが、期待の結果はexpとmyexpの結果が 同じものにならなければいけないみたいです・・・ テイラー展開の補足とヒントでもいいので プログラムの組み方を教えてください<_ _> おねがいします<_ _> #include<stdio.h> #include<math.h> double myexp(double); int main(void) { double x; printf(" x myexp(x) exp(x)\n"); for(x=0;x<=40;x=x+10) printf("%5.1f%14.6g%14.6g\n",x,myexp(x),exp(x)); } double myexp(double x) { double EPS=1e-08; double s=1.0,e=1.0,d=1.0,diff; int k; ----------------------------------- d=x*x; k=2; while (1){ x = x*d; s *= (k-1)*k; k += 2; diff = x/s; /* n項目の値 */ e += diff; if (fabs(diff) < EPS){ break; } } return e; ------------------------------ }

  • プログラムについて(C言語)

    #include<stdio.h> int main() { int i,n,total; for(;;){ /* 無限ループ*/ printf("整数n?"); /* nの値の表示 */ scanf("%d",&n); /* ifとbreakを使った終了判定 */ if(n<0)break; total=1; for(i=1;i<=n;i++){ printf("i=%d ",i); total*=i; /* total←total*i(階乗の計算) */ } printf("total=%d\n",total); /* totalの値(結果)の表示 */ } printf("Thanks\n"); /* 終了メッセージの表示 */ return(0); } これは階乗を求めるプログラムなのですが、i++ではなくi--をつかって求める場合 どのように変更すればよいでしょうか? for(i=1;i<=n;i++){ あたりをいろいろ弄ってみたのですが、求めてる結果は得られませんでした

  • sin(x)の近似について

    sinxの数値計算 任意のxに対するsinxの値をマクローリン展開を利用して近似し、誤差の限界(n番目の値が1*10^-8)になるまでもとめよ。 という問題なんですが、for文でいろいろやってみたのですが、n番目の値が1*10^-8までというのがどうしてもできません。 C言語です。 たぶん、階乗の部分が間違ってると思われます。 下のが自分で考えたものです。アドバイスいただけたら嬉しいです。 #include <stdio.h> int main(void) { signed int i,v; double x=-1; double a,m,n=1.0; double sinx = 0.0; a=x; v=1; printf("a=%f\n",a); printf("sinx = %f\n",sinx); while(1) { sinx = sinx + a; m=x; m=m*x; a = (-1)*v*m/((n+1)*(n+2)); n=n+2; v=v*(-1); if ((a < 1e-8) && (a > -1e-8)) break; } printf("sinx = %f",sinx); return (0); }

  • C言語課題(数値計算)お願いします。

    課題内容 ニュートン法を用いて√2の値を求められるプログラムを作る。 10ケタまで表示する。 書いてみたソースコード #include <stdio.h> #include <stdlib.h> #include <math.h> #define max 1000 //最大繰り返し回数 #define eps 1.0e-10 //収束条件 double f(double x); double df(double x); void newton(void); int main() { newton(); return 0; } void newton(void) { int count; double a,newa; count=0; printf("初期値を入力してください。\n"); scanf("%lf",&a); for(;;) { count++; newa=a-f(a)/df(a); if(fabs(newa-a)<eps) break; a=newa; if(count==max) { printf("収束しませんでした。\n"); exit(1); } } printf("解の値は %f\n収束するのに %d 回かかりました。\n", newa,count); } double f(double x) { return x*x-2; } double df(double x) { return 2x; } コンパイルできません。 間違いとどうすればよいかを教えていただけるとありがたいです!!!

  • プログラムで指数関数を求めようとする際の誤差

    プログラムで指数関数を求めようとする際の誤差について 以下のプログラムで指数関数を求めようとした時xを-19 以下にすると非常に大きい誤差がでます。 この原因を教えてください(正しい値を導き出す方法は知ってますのでこの原因のみの回答お願いします) double myexp(double x){ double e=0.0,w=1.0; int n=1; while( 1000 >n){ e = e + w; w = w * (x/n++); } return e; } 結果 %a.out -19 myexp :4.256655954090954081880640764730e-09 exp :5.602796437537267825983759641107e-09 %a.out -20 myexp :6.147561828914626023583183534035e-09 exp :2.061153622438557869941517139661e-09 %a.out -21 myexp :1.508431683113148797351905471952e-08 exp :7.582560427911906596506229561372e-10 %a.out -22 myexp :-1.418468652124354027877889519770e-08 exp :2.789468092868924638469287369269e-10 %a.out -23 myexp :-1.057684107493219557015354075069e-07 exp :1.026187963170189018316677204798e-10 %a.out -24 myexp :3.443053542880998592081419080319e-07 exp :3.775134544279097726131831689486e-11 %a.out -80 myexp :2.450820117045632320000000000000e+17 exp :1.804851387845415031401693585771e-35

  • 指数関数の求め方

    以下の二つのプログラムは指数関数を求めるものですが、前者ではxに-19以下の値を入れた際にまったく異なる解をだします。 これの原因を教えてください。 ----------------------------------------------------------- double myexp(double x){ double e=0.0,w=1.0; int n=1; while( n<1000){ e = e + w; w = w * (x/n++); } return e; } ----------------------------------------------------------- double myexp(double x){ double e=0.0,w=1.0; int n=1,f=0; if(x < 0){ f = 1; x = -x; } while(n<1000){ e = e + w; w = w * (x/n++); } if(f == 1){ e = 1 / e; } return e; } -----------------------------------------------------------

  • C言語プログラム(二分法)について質問です。

    C言語プログラム(二分法)について質問です。 以下作成したプログラムでは、aを入力すると, x=b - (log(a)) - (a+x)/2の解が求まります。(今回式は適当ですが。) 二分法で解を求めるというプログラムは作成できたのですが、 このプログラムで、aを1~10まで変化させたときのxの値というようなループをプログラムでしたうえで、 a*xの値がもっとも大い点を求めるためにはどのようなプログラムを組めば(これを改良すれば)いいのでしょうか? 具体的に行いたいことは、この二分法のプログラムをaとxの関数とし、a*xの最大点を求めたいのです。 質問が分かりにくいかもしれませんが、お願いします。 以下作成したプログラムです。 #include <stdio.h> #include <stdlib.h> #include <math.h> #define eps 1.0e-6 double b=0.3; double a=0; double f(double x); void nibun(void); int main() { nibun(); return 0; } void nibun(void) { int count; double x0,x1,m; printf("aの値\n"); scanf("%lf",&a); count=0; x0=-2*b; x1=b; do { count++; m=(x0+x1)/2.0; if(f(m)*f(x0)<0) x1=m; else x0=m; if(count==700) { printf("Error\n"); exit(1); } } while (!(fabs(x0-x1)<eps)); printf("解の値は %f\n",m); } double f(double x) { return(b - (log(a)) - (a+x)/2); }

  • 値の渡し方?(初心者)

    以前質問したプログラムについて、新たに質問です。 メインプログラムと、関数プログラムを組みました。 関数の中では、print文を使うと計算は正しく行われていて、結果が正しいことが分かりました。 でうが、メイン文の出力では、どこにも出てこない変な値が出てきてしまいます。 値の渡し方がおかしいのでしょうか? 誰か、アドバイスをお願いします。 ***以下プログラムです。*** #include <stdio.h> #include <math.h> double gamma(double x) { double c[9],y,a,r,b,s,z; int i; a=1.; r=1.; c[1]=5.771916e-01; c[2]=9.882058e-01; c[3]=8.970569e-01; c[4]=9.182068e-01; c[5]=7.567040e-01; c[6]=4.821993e-01; c[7]=1.935278e-01; c[8]=3.586834e-02; printf("0 %f\n",x); while(1){ if(x>2.){ x=x-1.; a=a*x; printf("1 %f %f\n",x,a); } else if(x<1.){ a=a/x; x=x+1.; printf("2 %f %f\n",x,a); } else{ break; } } x=x-1.; for(i=1;i<9;i++){ b=(double)(i); s=(c[i]*((double)(pow(-1.,b)))*((double)(pow(x,b)))); printf("3 %d %f\n",i,c[i]); r=r+s; } y=a*r; printf("4 %lf\n",y); return y; } main() { double x,y; printf("数字を入力してください。"); scanf("%lf",&x); printf("メインプログラム x= %lf \n",x); y=gamma(x); printf("x= %f y= %f\n",x,y); }

  • γ関数のプログラム(初心者です)

    以下のようにγ関数のプログラムを組みました。 とりあえず整数値を入力すれば、正しい値は返しているということがprintfの4で確認できました。 もとはfortranで組んだプログラムをCに置き換えました。 ですが、実際走らせてみると、4で値は確認できますがsegmentation faultが出てしまいます。 ですからサブルーチンファイル(ユーザー関数?)として利用できません。 何がいけないのでしょうか? 正しくyが帰ってくるようにどうなおしたらよいのか教えてください。 #include <stdio.h> #include <math.h> double gamma(double x) { double c[8],y,a,r,b,s; int i; a=1.; r=1.; c[1]=5.771916e-01; c[2]=9.882058e-01; c[3]=8.970569e-01; c[4]=9.182068e-01; c[5]=7.567040e-01; c[6]=4.821993e-01; c[7]=1.935278e-01; c[8]=3.586834e-02; printf("0 %f\n",x); while(1){ if(x>2.){ x=x-1.; a=a*x; printf("1 %f %f\n",x,a); } else if(x<1.){ a=a/x; x=x+1.; printf("2 %f %f\n",x,a); } else{ break; } } x=x-1.; for(i=1;i<8;i++){ b=(double)(i); s=(c[i]*((double)(pow(-1,b))) *((double)(pow(x,b)))); printf("3 %d %f\n",i,c[i]); r=r+s; } y=a*(r+(0.03586834*((double)(pow(-1,8)))*((double)(pow(x,8))))); printf("4 %f\n",y); return y; } main() { double x,y; printf("数字を入力してください。"); scanf("%lf",&x); printf("メインプログラム %lf\n",x); y=gamma(x); printf("%f\n",y); }

  • C言語 二分法

    初投稿です。 お恥ずかしながらパソコンが苦手で、Cゲッが難しくてできません。 今回二分法です。 途中まではやったのですができません。 演習 0~1の乱数を12個発生させ,これらの平均をxi,yi とする。(s,tは乱数) xi=1/12(x1i+x2i+....x12i) yi=1/12(y1i+y2i+....y12i) このようなを1000個作り,(xi,yi)で散布図 を作りなさい。またx,yのそれぞれの平均を求 めよ。 この演習で #include<stdio.h> #include<stdlib.h> #include<math.h> #define eps 1.0e-5 float f(double x); void nibuin(void); int main () { int count; double a,b,m; count=0; printf("範囲の左の値を入力してください。\n"); scanf("%lf",&a); printf("範囲の右の値を入力してください。\n"); scanf("%lf",&a); if(count==1000){ printf("収束しませんでした。\n"); exit(1); } } while(!(fabs(a-b)<eps)); printf("解の値は%f\n収束するのに%d回かかりました。"m,count); } float f(double x) { reurn x*sin(x)+log(x); } まではできたのですが、 scanf("%lf",&a);とif(count==1000){の間に入る命令が打てません。 よろしくお願いします。