• ベストアンサー

C言語の実数型の足し算

C言語初心者です。関数の勉強していて、実数型計算に出くわしました。 #include <stdio.h> float add(float a, float b) { return a+b; } int main(void) { float x=10.5,y=20.3; printf("%f %f\n",x,y); printf("%f\n",add(x,y)); return 0; } としたら、 10.500000 20.299999 30.799999 という結果になりました。今のところint型でずーっと勉強していたので、20.3の20.299999表記が怪しく感じられ、結果も同様に怪しく感じられます。どうして、10.5+20.3=30.8とすっっきり表示してくれないのでしょうか。

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

  • ベストアンサー
  • yama1718
  • ベストアンサー率41% (670/1618)
回答No.4

二進数はわかりますよね、桁が増える毎に倍の値になります。 1 → 1 2 → 10 3 → 11 4 → 100 それで、二進数には少数もあるのです、割り切れる値で言うと、 桁が下に増える毎に半分の値になります。 0.5 → 0.1 0.25 → 0.01 0.125 → 0.001 その二進数では 0.1 ですら割り切れない近似値になってしまいます。 0.1 → 0.00011001100110011001100… → 0.099999904632568359375… なぜ、この様に誤差が出るのに少数を二進数で計算しているかは、 速度を優先して演算プロセッサがその様に計算する仕様になっているからです。 金融計算では小数点以下の誤差も問題になりますから別に通貨型と呼ばれる。 内部では値を100倍とか10000倍などにして扱う固定小数点型を使用します。 他にはBCD演算と言って元から10進数で計算する方法もありますが、 計算速度がかなり遅いのであまり使用されていません。 CやC++でもBCD演算ライブラリを使用すれば誤差の少ない計算ができます。 (確かBorland C++ のオプションにBCD演算のライブラリがあったかな)

参考URL:
http://www.cc.kyoto-su.ac.jp/~yamada/pB/float.html
inocchi210
質問者

お礼

速度とプロセッサの関係、固定小数点やBCD演算と、本来ならC言語の勉強をする前にしておかなければならなかったのですね・・・実務的なお話とても参考になりました、ありがとうございました。

その他の回答 (5)

  • tadys
  • ベストアンサー率40% (856/2135)
回答No.6

コンピュータの中での計算は2進数で行っていますが 2進数で3/10を計算すると循環小数になってしまいます。 しかしながら、コンピュータで扱える数値の桁は有限なので 無限に続く数値を正確に表す事はできません。 0.3=3/10を2進数を用いて筆算の方法で計算してみると 11/1010   2進数 1回目       0.01      _______ 1010/11.00      10 10     ――――――――         10 2回目       0.01001      ________ 1010/11.00      10 10     ――――――――         10000          1010     ―――――――――           110 となって、途中で11(10進数の3)が再び現れるので循環小数になります。 10進数で1/3=0.33333・・・・・ となる計算も 3進数で計算すれば 1/10 = 0.1 となって有限の小数になります。

inocchi210
質問者

お礼

筆算の丁寧な記述、どうもありがとうございました。今度からプログラムを勉強するときは、コンピュータ内部で2進計算されているんだということを意識してしようと思いました。循環小数は3進数との関係も、今後勉強していきたいと思います、ありがとうございました。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.5

二進数と十進数の変換時に生じる誤差のこともありますが、元々有効桁数が小数点以下1桁しかないのだから、小数点以下1桁までしか意味をなさないのは当然です。 printf("%.1f %.1f\n",x,y); printf("%.1f\n",add(x,y)); と書けばよいのではないでしょうか? ちなみに、C言語における「実数型」というのは、虚数型(_Complexや_Imaginary)に対しての実数型ですので、整数型も含みます。

inocchi210
質問者

お礼

誤差、有効桁、実数型や虚数型があることを理解できました、ありがとうございました。

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.3

浮動小数点数のうち、2のべき乗の和の形で表わせない数値(例えば今回の20.3)を 有限のバイト数で表現しようとするとき、必ず誤差を伴います。 厳密な数値である20.3が、コンピューターの中でも厳密に20.3である、 という保証はどこにもありません。

inocchi210
質問者

お礼

20.3がコンピュータ内部ですっきりした20.3でないというのは少し驚きましたが、浮動小数点や誤差の話とCのプログラムのつながりがわかりましたので、ありがとうございました。

  • Tasuke22
  • ベストアンサー率33% (1799/5383)
回答No.2

結論を先に書けば、実数型では0.3が無いからです。 常に概数の表現であることを踏まえなければなりません。

参考URL:
http://ja.wikipedia.org/wiki/%E5%AE%9F%E6%95%B0%E5%9E%8B
inocchi210
質問者

お礼

コンピュータ内部が概算であることに気がつきました、確かに、無限の数字を有限にするということを今度から考えてプログラミングの勉強をしたいと思います。ありがとうございました。

  • notnot
  • ベストアンサー率47% (4848/10261)
回答No.1

実数は内部で十進数でなく二進数で保持されていることはご存じでしょうか? 0.3 や 0.1 は二進数では循環小数になるので、有限桁では正確に表現できません。0.5 とか 0.25 0.75 0.125 などは正確に表現できます。 他の例では例えば、1/3 は十進数だと循環小数ですが、三進数だと小数点以下一桁です。

inocchi210
質問者

お礼

r進数の勉強は苦手だったので、参考程度に読み飛ばしてC言語教科書を読んでいました。コンピュータ内部での実数が2進法との兼ね合いで、Cのプログラムの結果にまで出てくるとは少し驚きでした。参考になりました、ありがとうございました。

関連するQ&A

  • C言語の質問です。

    #include"stdio.h" int main(void){ int a, b, add; scanf_s("%d%d", &a, &b); add = a+b; printf("add=%d\n", add); return 0; } と、------------------------------------------------------------------------------ #include"stdio.h" int tasizan(int x, int y); int main(void){ int a, b, add; scanf_s("%d%d", &a, &b); add = tasizan(a, b); printf("add=%d\n", add); return 0; } int tasizan(int x, int y){ int aa; aa = x + y; return aa; } の違いを教えてください。

  • C言語 プログラミングで行詰まりました…

    標準入力(キーボード)からi,jk,nの値を入力し、次の漸化式を計算し、X_0からX_nまで求めるプログラムを作成したいのですが、うまく表示されません。どかがおかしいのかご指摘お願いします。 <漸化式> X_n=(a+b)/X_(n-1) , X_0=c(n=0) ================================================================== #include<stdio.h> float f_X(int a,int b,float c) { float y; y=(a+b)/c; return y; } int main (void) { int number,i,j; float k,l,n,X; printf("i:"); scanf("%d", &i); printf("j:"); scanf("%d", &j); printf("k:"); scanf("%f", &k); printf("n:"); scanf("%f", &n); X=k; printf("X_0= %.6f\n",X); for(number=1;number<=n;number++) { l=f_X(i,j,X); printf("X_%d= %.6f \n",number,l); X=l; } return 0; } ===================================================================

  • c言語で

    c言語で /*a==bのとき商と余を求めるそれ以外は積を求める*/ # include<stdio.h> main () { int a,b,seki,sho,joyo; printf("aとbを入力してください\n"); scanf("%d %d", &a,&b); if(a==b) printf("sho=%d\n",(a/b)); printf("joyo=%d\n",(a%b)); else printf("seki=%d\n",(a*b)); return(0); } はどこがおかしいでしょうか? あと /*小数点*/ # include<stdio,h> main () { float a,b,seki,sho,joyo; printf("%f %f",&a,&b); if(a>b) printf("sho=%f4.3\n",(a/b)); printf("joyo=%f4.3\n",(a%b)); else printf("seki=%f4.3\n",(a*b)); return (0) にいたってはエラーの嵐なのですがどうすればいいでしょうか

  • C言語で入力した実数の絶対値を求める

    現在C言語を勉強し始めたばかりなので 協力お願いします。 今回書きたいのは題名にある通り 実数の絶対値を求めるプログラミングです。 ソースは以下の通りです。 #include <stdio.h> main(){ float x,z; printf("Input Nonber\n"); scanf("%f",&x);      if( A >= B ) z=C else z=D; printf("Answer=\n",z); } この中で条件付するときの A,B,C,Dをどうすればいいか分からないので 教えてください。 また書いているもので間違えなどがあれば ご指摘していただけたら嬉しいです。

  • C言語の関数に関する質問ですが

    C言語の初心者です。よろしくお願いいたします。 授業でこのような演習が出ました。 演習:実数x を入力したときの最大値を求めるプログラムを作れ. 実数x を入力すると,x; -x; x2; xの絶対値の平方根 の中で一番大きい値を答える プログラムを作れ(ファイル名はmax.c とする). 表示は以下のようにする. Input x: -0.5 【Enter】 Answer is 0.707107. #include<stdio.h> #include<math.h> double max(double a, double b){ if( a > b) return a; else return b; } int main(void) { double x,y; printf(\"Input x: \"); scanf(\"%lf\",&x); y = max (x,-x); y = max (y,x*x); y = max (y,sqrt(fabs(x))); printf(\"Answer is %f.\\n\",y); } このように書けばうまく実行できますが、関数の中に関数を使えないでしょうか。うまく言えないですが、たとえば、以下のように書いてみましたが、うまく実行できません。どう直したらいいでしょうか、お忙しい中教えていただけたらうれしいです。 #include <stdio.h> #include <math.h> int max(double a,double b) { if (a<b) return b; else return a;} int main(void) { double x,result; printf(\"Input x:\"); scanf(\"%lf\",&x); result=max(max(x,-x),max(pow(x,2),sqrt(fabs(x)))); printf(\"%.2f\",result); return 0; } よろしくお願いいたします!!

  • C言語のプログラム

    C言語で2つの微分方程式をEuler法、Heun法、Runge-Kutta法により求めるプログラムを作りたい。ただし、初期条件はx=0,y=1とする。また、間隔Δxを変えたときの解の変化を調べたい。 Euler法のプログラムはどうにか分かったのですが、Heun法、Runge-Kutta法のプログラムがわかりません。 Euler法のプログラム #include_<stdio.h> #include_<math.h> int_main(){ __double__a=0; __double__b; __int_____m=10; __int_____n; __double__h; __double__x,y; __double__dydx; __int_____k; __double__e; __double__f; __printf("オイラー法計算例:y=e^x,_y=1/e^4x\n\n"); __//_y_=_e^x __b_=_1; __for(n=100;n<=10000;n*=100){ ____h_=_(b-a)/n; ____printf("y'_=_y:_h(=dx)_=_%.1e_(y=e^x)\n",h); ____x_=_a;_y_=_1; ____for(k=0;k<=n;k++)_{ ______x_=_k*h; ______if(k%(n/m)==0)_{ ________f_=_exp(x); ________e_=_fabs(y-f); ________printf("x=%.2f,_y=%f,_e^x=%f_er=%.0e\n", ________x,y,f,e); ______} ______dydx_=_y; ______y_=_y_+_dydx*h; ____} __} __printf("\n"); __//_y_=_1/e^4x __b_=_4; __for(n=100;n<=10000;n*=100){ ____h_=_(b-a)/n; ____printf("y'_=_-4y:_h(=dx)_=_%.1e_(y=1/e^4x)\n",h); ____x_=_a;_y_=_1; ____for(k=0;k<=n;k++)_{ ______x_=_k*h; ______if(k%(n/m)==0)_{ ________f_=_exp(-4*x); ________e_=_fabs(y-f); ________printf("x=%.2f,_y=%f,_1/e^4x=%f_er=%.0e\n", ________x,y,f,e); ______} ______dydx_=_-4*y; ______y_=_y_+_dydx*h; ____} __} __return_0; } 分かる方がいましたら、回答よろしくお願いします。

  • C言語で・・

    実数データを入力し、小数点以下四捨五入したときの値を表示せよ。 自分では、以下のように作りました。 #include<stdio.h> int main(void) { float f_a,s; int i_a; printf("実数データを入力してください"); scanf("%f",&f_a); i_a=(int)f_a; s=f_a-i_a; if(s>=0.5){ i_a=i_a+1; } printf("%fを四捨五入すると%dです",f_a,i_a); return 0; } 他の作りかたがあれば、教えていただきたいのですが。IF文などの範囲でお願いします。

  • C言語のポインタの考え方について

    ポインタについて理解ができていないのでお聞きしたいのですが 値を交換する関数のプログラミングでこの場合ポインタ で以下にしないといけないと思います。 #include<stdio.h> void swap(int *a int *b){ int c; c=*a; *a=*b; *b=c; } main(){ int x,y; x=123; y=456; swap(&x,&y); printf("x = %d, y = %d\n", x, y); } またポインタを使用せず以下のプログラムではなぜダメのでしょうか。 よろしくお願い致します。 #include<stdio.h> void swap(int a int b){ int c; c=a; a=b; b=c; } main(){ int x,y; x=123; y=456; swap(x,y); printf("x = %d, y = %d\n", x, y); }

  • C言語 文字列の比較 compare

    プログラミング初心者です。 60文字以内の文字列を入力して、 大小関係を比較・表示するプログラムなのですが・・ 「AはBより大きい」という結果しか出ません。 どこが間違っているのか、ご指摘お願いしますっ。 #include<stdio.h> int main(void) { char moji1[61]; char moji2[61]; printf("文字列Aを入力===>"); scanf("%60s" ,&moji1); printf("文字列Bを入力===>"); scanf("%60s" ,&moji2); if(moji1-moji2>0){ printf("===AはBより大きい===\n"); } else if(moji1-moji2<0){ printf("===AはBより小さい===\n"); } else if(moji1-moji2==0){ printf("===AとBは等しい===\n"); } return 0; } int compare(char *x, char*y) { while(*x==*y){ if(*x=='\0') return 0; x++; y++; } return (*x-*y); }

  • c言語について

    C言語で、二つの整数値を読み込んで、前者の値が後者の何%であるかを実数で表示するプログラムを作成しようとして以下のコードを書きました。 #include <stdio.h> int main(void) { int n1, n2 ; puts("二つの整数を入力してください。") ; printf("整数A : \n") ; scanf("%d" , &n1) ; printf("整数B : \n") ; scanf("%d" , &n2) ; printf("Aの値はBの%f%%です。\n" , (double)(n1 / n2) * 100) ; return 0 ; } 上記コードを実行すると、0.000000%のような結果になります。 そこで、最後のキャスト演算子を使用した後の式で(n1 / n2) * 100がまずいのかなと考え、n1 / n2 * 100にしたら上手くいきました。 なぜ、上記コードだと上手くいかないんでしょうか?

専門家に質問してみよう