• ベストアンサー

2つ分数の四則演算を行うプログラム

<要求事項> 分数は、 例)1|3 のように表す。 1.分母がゼロの時はエラーとする。 2.除算において、除数がゼロの入力エラーに対しては、再入力するように促す。 3.以下範囲の整数(分子、分母にかかわらず)に対して、正しく計算できるようにする。   -2147483648 ~ 2147483647 4.計算結果については,分母が1の時には分子のみ表示。分数がの時には0のみを表示。最終計算結果は既約分数にする。分数が負数の場合、-を分数の前に表示。 #include <stdio.h> #include <math.h> int main(void) { int b,d; /* 分母*/ int a,c; /* 分子*/ int sign,sign2,sign3,sign4; int yakusu,yakusu2,yakusu3,yakusu4; /* 最大公約数*/ printf("分母= "); scanf("%d",&b); printf("分子= "); scanf("%d",&a); if(b==0){ printf("分母が0です。入力が誤っています。\n"); /*分母が0ならエラーとする*/ return 0; } if (a==0){ printf("分数1= 0\n"); /*分子が0のとき*/ } else{ printf("分数1=%d|%d\n",a,b); /*一つ目の分数*/ } printf("\n"); printf("分母= "); scanf("%d",&d); printf("分子= "); scanf("%d",&c); if(d==0){ printf("分母が0です。入力が誤っています。\n"); /*分母が0ならエラーとする*/ return 0; } if (c==0){ printf("分数2= 0\n"); /*分子が0のとき*/ } else{ printf("分数2=%d|%d\n",c,d); /*二つ目の分数*/ } printf("\n"); /* 足し算:a|b + c|d = (a*d + b*c) | (b*d) */ printf("足し算:%d|%d + %d|%d\n",a,b,c,d); yakusu = gcd(abs(a*d + b*c),abs(b*d)); /*最大公約数を求める*/ sign = (a*d + b*c)/abs(a*d + b*c) * (b*d/abs(b*d)); if(sign * abs(a*d + b*c)/yakusu == 0){ /*分子が0となる時*/ printf("0\n"); } if (abs(b*d)/ yakusu!= 1){ printf("既約分数は %d|%d\n" ,sign * abs(a*d + b*c)/yakusu , abs(b*d)/yakusu ); } else{ printf("既約分数は %d\n" ,sign * abs(a*d + b*c)/yakusu ); /*分母が1の場合*/ } printf("\n"); /* 引き算:a|b - c|d = (a*d - b*c) | (b*d) */ printf("引き算:%d|%d - %d|%d\n",a,b,c,d); yakusu2 = gcd(abs(a*d - b*c),abs(b*d)); /*最大公約数を求める*/ sign2 = (a*d - b*c)/abs(a*d - b*c) * (b*d/abs(b*d)); if(sign2 * abs(a*d - b*c)/yakusu2 == 0){ /*分子が0となる時*/ printf("0\n"); } if (abs(b*d)/ yakusu2!= 1){ printf("既約分数は %d|%d\n" ,sign2 * abs(a*d - b*c)/yakusu2 , abs(b*d)/yakusu2 ); } else{ printf("既約分数は %d\n" ,sign2 * abs(a*d - b*c)/yakusu2 ); } printf("\n"); /* 掛け算:a|b * c|d = (a*c) | (b*d) */ printf("掛け算:%d|%d * %d|%d\n",a,b,c,d); yakusu3 = gcd(abs(a*c),abs(b*d)); /*最大公約数を求める*/ sign3 = (a*c)/abs(a*c) * (b*d/abs(b*d)); if(sign3 * abs(a*c)/yakusu3 == 0){ /*分子が0となる時*/ printf("0\n"); } if (abs(b*d)/ yakusu3!= 1){ printf("既約分数は %d|%d\n" ,sign3 * abs(a*c)/yakusu3 , abs(b*d)/yakusu3 ); } else{ printf("既約分数は %d\n" ,sign3 * abs(a*c)/yakusu3 ); /*分母が1の場合*/ } printf("\n"); /* 割り算:a|b / c|d = (a*d) | (b*c) */ printf("割り算:%d|%d / %d|%d\n",a,b,c,d); yakusu4 = gcd(abs(a*d),abs(b*c)); /*最大公約数を求める*/ sign4 = (a*d)/abs(a*d) * (b*c/abs(b*c)); if(sign4 * abs(a*d)/yakusu4 == 0){ /*分子が0となる時*/ printf("0\n"); } if (abs(b*c)/ yakusu4!= 1){ printf("既約分数は %d|%d\n" ,sign4 * abs(a*d)/yakusu4 , abs(b*c)/yakusu4 ); } else{ printf("既約分数は %d\n" ,sign4 * abs(a*d)/yakusu4 );/*分母が1の場合*/ } return 0; } int gcd(int x,int y){ /* ユークリッド互除法*/ int z; if( (x <= 0) || (y <= 0) ){ return -1; } z = x % y; while (z != 0){ x = y; y = z; z = x % y; } return y; } >>大学の課題です。現在の状態は上記の通りです。このプログラムだと、答えの既約分数が0になると表示できなかったり、桁が大きい数で計算しようとすると値がおかしくなってしまいます。 どなたかプログラム改良にご助力願えないでしょうか?

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

  • ベストアンサー
  • arain
  • ベストアンサー率27% (292/1049)
回答No.6

No.2です >オーバーフローというものは良く分からないのですが、アドバイスを踏まえて色々やってみます。 オーバーフローの概念だけ簡単に。 例えば、0~99までが入る変数型「sample」があったとします(ここでわかりやすく10進数でいきます) sample a, b, c; a = 99; b = 99; c = 0; c = a * b; という計算を行った場合、cの値はいくつになるでしょうか? 単純に「a * b」を計算すると「99 * 99 = 9801」となります。 しかし、sample型で扱うるの「0~99」です。 そのため100以上の値は「c」に格納することができず桁あふれ(オーバーフロー)が発生してしまいます。 このため、桁あふれ分はcに格納することができず「c = a * b」の結果「c」には桁あふれで残った(sample型に格納できる)分「1」が入ることになります。 注:あくまで概念の説明です。

zinrong
質問者

お礼

回答ありがとうございます。オーバーフローの概念を理解することができ、課題もなんとか終わらせることができました。分かりやすい説明ありがとうございました。

その他の回答 (5)

  • yama5140
  • ベストアンサー率54% (136/250)
回答No.5

>桁が大きい数で計算しようとすると値がおかしくなってしまいます。    ・今回の質問では、標準入力からの分母と分子は int なので、内部では、-2147483648 ~ 2147483647 と「正しく」扱われる。  ・この分母、分子を「計算(かけ算、たし算)しよう」とした時、「おかしく」なる。    例)2147483647 + 1 = -2147483648  -1 * -2147483648 = -2147483648  ・「おかしく」なる理屈は、分母、分子、丸ごとスタックにおくられ、演算が行われる際に、有限のスタック(今回は32ビット)に【納めきれなくなる】。   ☆そんなら、丸ごとスタックにやらず、1桁ずつ「計算」をすればよいのでは・・。  例えば、筆算で、     123  ×234    の計算をするとき、まず3×4をやって12の2を・・と下位から1桁ずつ確定していきますよね。  この過程をコード化すればよいのではと・・。    で重要なのは、この結果は、「表示」にしか用いない(というか用いられない)。  今回の質問内容では、これで十分ですよね↑・・。  巷のπの計算だって、何万桁まで「内部で扱う数」としてでなく、「結果表示」でのみ有効なものですよね。  でないと、正しいか検証もできないし・・。 ------------------------------------------------ ★「足し算」、「かけ算」について、筆算もどきのソースを投稿します(BorlandC++5.6.4)。  「値がおかしくなって」しまう数で呼び出してみて下さい。 ・「足し算」は、オーバーフローするか判定し、するものについて筆算もどきしてます。   判定:「演算」で、正+正が負のとき、負+負が正のときをオーバーフローとしています。 ・「かけ算」は、上述の方法です。 共に、20個の配列(20桁)で「筆算もどき」しています。 ------------------------------------------- #include <stdio.h> #include <stdlib.h> void Output( char cKekka[] ) {  int i, iSumi = 0;  for( i = 0; i <= 20; i++ ){   if( ( 0 == iSumi ) && ( 0x00 == cKekka[ i ] ) ) continue;   printf( "%c", ( 0x30 + cKekka[ i ] ) );   iSumi = 1;  }  printf( "\n" ); } void Tashizan( int iVal1, int iVal2 ) {  int iOver = 0, kk = 20, iDum;  char cKekka[32] = { 0x00 };  if( iVal1 <= 0 ){ // オーバーフロー判定   if( iVal2 < 0 ){ // 双方負    iOver = iVal1 + iVal2;    if( iOver >= 0 ) iOver = 1;   }  }  if( iVal1 > 0 ){ // オーバーフロー判定   if( iVal2 > 0 ){ // 双方正    iOver = iVal1 + iVal2;    if( iOver <= 0 ) iOver = 2;   }  }  if( ! iOver ){ // オーバーフローしない:通常「演算」   printf( "%d\n", ( iVal1 + iVal2 ) );  }  if( iOver ){ // オーバーフローするので筆算もどき   while( iVal1 || iVal2 ){ // 1桁ずつの足し算    iDum = cKekka[ kk ];    iDum += abs( iVal1 % 10 ); // 下1桁    iDum += abs( iVal2 % 10 );    cKekka[ kk ] = (char)( iDum % 10 );    cKekka[ kk - 1 ] = (char)( iDum / 10 ); // 繰り上がり    kk--; // 上位の桁へ    iVal1 /= 10;    iVal2 /= 10;   }   if( 1 == iOver ) printf( "-" );   Output( cKekka );  } } void Kakezan( int iVal1, int iVal2 ) {  int i, j, kk, iTotal = 0;  char c1[ 32 ] = { 0x00 }, c2[ 32 ] = { 0x00 };  char cKekka[ 32 ] = { 0x00 };  if( iVal1 < 0 ) c1[ 31 ] = 0x01; // 負  if( iVal2 < 0 ) c2[ 31 ] = 0x01;  for( i = 20; i >= 0; i-- ){ // 1桁ずつ配列へ   c1[ i ] = (char)abs( iVal1 % 10 );   c2[ i ] = (char)abs( iVal2 % 10 );   iVal1 /= 10;   iVal2 /= 10;  }  for( i = 20; i >= 0; i-- ){ // 下位から筆算もどき   for( j = 20; j >= 0; j-- ){ // 被乗数桁    kk = j - ( 20 - i ); // 格納桁    if( ( kk - 1 ) <= 0 ) break;    cKekka[ kk ] += (char)( c1[ j ] * c2[ i ] );    cKekka[ kk - 1 ] += (char)( cKekka[ kk ] / 10 ); // 繰り上がり    cKekka[ kk ] = (char)( cKekka[ kk ] % 10 );    iTotal += cKekka[ kk ];   }  }  cKekka[ 31 ] = (char)( c1[ 31 ] + c2[ 31 ] ); // 符号:奇数なら負  if( iTotal && ( cKekka[ 31 ] % 2 ) ) printf( "-" );  if( 0 == iTotal ) printf( "0" );  Output( cKekka ); } 注:インデントに全角空白を用いています。タブに一括変換して下さい。

zinrong
質問者

お礼

回答ありがとうございます。課題のほうですが、何とか終わらせることができましたので、お書き頂いたプログラムはこれからの学習に役立てていきたいとます。

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.4

反則になるかもしれませんが、 1.64ビットの整数がつかえるなら、それを使用されてはいかがですか。 linuxならlong long int型になります。 これで、約20桁の数値が格納できます。 2.それでも足りないなら、多倍長演算のライブラリを使用します。 これで、メモリが許す限り、無限桁の数値が扱えます。 参考URLを参照下さい。(多倍長演算のライブラリの一例です) 3.また、C言語でなくてよいなら、rubyで計算することを推奨します。 rubyの変数は、上限及び下限がありません。四則演算程度なら、半日でrubyの文法は覚えられます。

参考URL:
http://mitv2.net/software/tiny_mp/tiny_mp.html
zinrong
質問者

お礼

アドバイスありがとうございます。 long int~の話は初耳だったので試してみます。URLの方も参考にさせていただきます。

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

「桁が大きいと結果がおかしい」というのは, 多分オーバーフローですね. ことあるごとに gcd を使って約分していくしかないと思います. 例えば a/b + c/d を計算するのに, (ad + bc) / (bd) としてしまってはダメで, 分母を最初から b と d の最小公倍数にする必要があります. とはいっても, これだけで本当に十分とは思えないんですが....

zinrong
質問者

お礼

回答ありがとうございます。 正直なところ、gcd関数はあまり理解できていないのですが、もう少しがんばって考えてみます。

  • arain
  • ベストアンサー率27% (292/1049)
回答No.2

>桁が大きい数で計算しようとすると値がおかしくなってしまいます の部分ならある程度推測できます。 「int * int」の式が多いですが、この乗算の結果がintの最大値をオーバーフローしている場合に問題が出ていませんか? オーバーフローしないように工夫する必要はあります。 例えば sign3 = (a*c)/abs(a*c) * (b*d/abs(b*d));  ↓ sign3 = (a/abs(a) * c/abs(c)) * ((b/abs(b) * d/abs(d)); とか(元の計算式があってるかは確認していません)

zinrong
質問者

お礼

回答ありがとうございます。 オーバーフローというものは良く分からないのですが、アドバイスを踏まえて色々やってみます。

  • aigaion
  • ベストアンサー率47% (287/608)
回答No.1

気になる点 ・ ユークリッド互除法gcdの入力の制約を満たしていない  xがy以上.x,yは自然数の制約.  引数の0であるのにgcdを呼ぶ場合があるのでは? ・符号のチェックが少しわかりにくい  sign = (a*d + b*c) / (b*d);  sign = sign / abs(sign);  こうしたほうがあとから見てわかりやすいのでは? ・冗長な部分が多い  分数の入力,計算結果の表示は関数にできますね.

zinrong
質問者

お礼

ご指摘ありがとうございます。 アドバイスを踏まえ、もう一度再考した結果、より短くまとめることができました。

関連するQ&A

  • 既約分数の表示プログラム

    (1)キーボードから,分子,分母に相当する整数2つを入力し,その既約分数を表示せよ。 (2)分母が1の時には,分子のみを表示する。 (3)分子と分母の符号が異なるときにのみ,-符号を表示する。 (4)分母がゼロの入力エラーに対しては、再入力するように促す。 (5)分子と分母の最大公約数も求めて表示する。 (6)また、正しく計算できる最大規約分数を示せ。 #include <stdio.h> int main(void) { int a,b,i=1,x,y,z; printf("分子=");/*分子の入力*/ scanf("%d",&a); printf("分母=");/*分母の入力*/ scanf("%d",&b); if(b==0) { printf("分母が0です。入力が誤っています。\n"); return 0; } if(b==1) { printf("既約分数は %d\n",a); return 0; } while((i<=a)&&(i<=b)) { if((a%i==0)&&(b%i==0)) { x=i;i=i+1; /*xを上書きしていく*/ } else { i=i+1; } } printf("分子と分母の最大公約数=%d より\n",x); y=a/x; z=b/x; printf("既約分数は %d/%d\n",y,z); return 0; } 大学の課題で出されたものです。(1)(2)(4)(5)はできたのですが、(3)と(6)の部分のやり方がいまいちよくわからなかったので質問しました。 どなたかご教授お願いできないでしょうか・・・。

  • 分数を表示するプログラム(長文です)

    (整数値)aの値と(整数値)bの値をキーボードから入力して、そこから、a/bという分数を作るプログラムを書きたいと思います。(符号や約分も考えた形にする) 僕は、ヒントを参考に以下のようなプログラムを書いたのですが、ある条件の下では、正しく動きません。 ・aの値が負の場合 ・aもbも負の値の場合 など・・・ #include <stdio.h> #include <math.h> int main ( void ) { int a,b; int k,min,sign; printf("a=?");scanf("%d",&a); printf("b=?");scanf("%d",&b); if (a*b<0) sign = -1; else sign = 1; a = sign*a ; b = sign*b; if (abs(a)>abs(b)) min = b; else min = abs(a); for ( k=2 ; k<=min ; k++){ if (min%k == 0){ a = a/k; b = b/k; } } printf("a/b = %d",a); if (b != 1) printf("/%d\n",b); else printf("\n"); return 0; } これをどのように修正すれば、正確な答えが出るのでしょうか? 教えてください。

  • C言語における複素数の四則演算について

    複素数の四則演算(a+biとc+diの四則演算)について、for文を用いて表示するプログラムについて、???の部分に何を入れたらよいかわからず、うまく実行することができません。和・差・積・商の計算種別を入れるみたいなのですが、何を入れたらいいのかわかりません。 #include <stdio.h> void fukuso(double a,double b,double c,double d,double *e,double *f,int keisan); int main(void) { double a=4, b=8, c=4, d=3, e, f; int i; for(i=1;i<5;i++){ fukuso(a,b,c,d,&e,&f,???); if(i==1) printf("和演算\n"); else if(i==2) printf("差演算\n"); else if(i==3) printf("積演算\n"); else printf("商演算\n"); printf("e=%f f=%f i\n",e,f); } return (0); } void fukuso(double a1,double b1,double a2,double b2,double *a3,double *b3,int keisan) { if(keisan==1){ *e=a+c; *f=a+c; } else if(keisan==2){ *e=a-c; *f=b-d; } else if(keisan==3){ *e=a*c-b*d; *f=a*d+c*b; } else{ *e=(a*c+b*d)/(c*c+d*d); *f=(-a*d+c*b)/(c*c+d*d); } }

  • このプログラム見てほしいです!!

    #include <stdio.h> int gcd2(int a, int b) { if (!b) return a; return gcd2(b, a%b); } int main() { int a, b, c; printf("2つの任意の整数を入力せよ:"); scanf("%d %d",&a,&b); c=gcd2(a,b); printf("最小公倍数は%d\n",a*b/c); printf("最大公約数は%d\n",c); return 0; } で、最小公約数を出すことはできたのですが、全ての公約数を表示させたいんです!!どうやったらいいのでしょうか??プログラミングまだ初心者なので、ちょっと行き詰ってしまいました。。。 お時間があればでいいのですが、もう一つわからないプログラムがあります。 自然数nを入力し、x^2+y^2=z^2 (x<y)を満たすようなn以下の自然数の組(x,y,z)がいくつあるのかを出力するプログラムなのですが、全くわからず行き詰っています。。どなたかお時間があれば教えて頂きたいです。 色々と申し訳ありません。お願いします(__)

  • 分数の足し算をさせるプログラムが分かりません。どなたか分かりませんか?

    分数の足し算をさせるプログラムが分かりません。 C言語の問題で分数の足し算までは一応できるんですが、答えがでたときに整数で出すやり方と約分して表す方法が分かりません。 どなたか知恵を貸してくれませんか? ユーザから4つの整数を入力し、はじめに入力された2個の整数と後に入力された2個の整数を分数と考え、その分数の和を表示するプログラムを作成せよ。 例えば、「3」「4」「5」「6」と入力されたときは、3/4 + 5/6を計算する。 そのプログラム内では分数の和を計算する関数を作成する。 さらに、 約分を行う関数を 再帰呼び出しを利用して作成する。 void yakubun(int *a1, int *a2) 例えば、以下の場合1/2と表示される。 int i=10,j=20; yakubun(&i,&j); printf(“%d / %d”, i, j); ちなみにここまでできました↓ #include<stdio.h> void bunsu_tasizan(int a1,int a2,int b1,int b2, int *c1,int *c2 ) { *c1=(a1*b2)+(b1*a2); *c2=(a2*b2); } int main() { int x1,x2,y1,y2,z1,z2; printf("整数を入力してください"); scanf("%d",&x1); scanf("%d",&x2); scanf("%d",&y1); scanf("%d",&y2); if(x2==0||y2==0||x2==0&&y2==0) printf("0以外を入力してください"); else{ bunsu_tasizan(x1,x2,y1,y2,&z1,&z2); printf("%d/%d",z1,z2);} return (0); }

  • 整数を3つ読み込み、一番大きいものを表示するプログラム

    3つが違う数であるとしてこうしたんですが、 #include<stdio.h> int main() {int a,b,c; scanf("%d",&a); scanf("%d",&b); scanf("%d",&c); if(a>b && a>c){ printf("%d\n",a);} if(b>a && b>c){ printf("%d\n",b); if(c>a && c>b){ printf("%d\n",c); }}return 0;} で、コンパイルはできたんですが、実行できません。3つの数値を入力してもその一番大きい数が出てきません。ifの条件は間違ってはいないと思うんですがやはり、最大が2つあるときのことを考えないとできませんか?

  • 四則演算のプログラム

    自力で解きたかったのですが、二時間考えても、とけないので 投稿させていただきます。どこを直せばよいのでしょうか 実行するときは%プログラム名 + 5 12 と入力して実行したいです。加えて参照にしている部分の %の扱いについても教えていただけると幸いです。 #include<stdio.h> #include<stdlib.h> int main(int argc,char *argv[]){ char kigou; double num1,num2; kigou=atof(argv[1]); num1=atof(argv[2]); num2=atof(argv[3]); if(kigou=='+'){ printf("%f+%f=%f\n",num1,num2,num1+num2); } else if(kigou=='-'){ printf("%f-%f=%f\n",num1,num2,num1-num2); } else if(kigou=='x'){ printf("%f*%f=%f\n",num1,num2,num1*num2); } else if(kigou=='/'){ printf("%f/%f=%f\n",num1,num2,num1/num2); } /* else if(kigou=='%'){ printf("%f%%f=%f\n",num1,num2,num1%num2); }*/ else{ printf("Please input +,-,x,%%.\n");} }

  • Cのプログラム(初心者)

    #include<stdio.h> void main(void) { int a,b,c; printf("西暦で生まれた年を入力してください。\n"); scanf("%d",&a); printf("今の年を西暦で入力してください。\n"); scanf("%d",&b); c=b-a; if('0'<=c&&c<='23'){ printf("あなたは%d歳です。げんき。\n",c); } else if('24'<=c&&c<='40'){ printf("あなたは%d歳です。仕事盛り。\n",c); } else if('41'<=c&&c<='60'){ printf("あなたは%d歳です。まだいける。\n",c); } else if('61'<=c&&c<='99'){ printf("あなたは%d歳です。がんばれ。\n",c); } else{ printf("100歳以上なの?\n"); } } というプログラムを作っているのですが、うまく実行できません。間違いがあると思うのですが、それもよくわかりません。ビルドは成功するのですが、どの数字を入力しても「げんき」しか出てきません。 わかる人がいたら教えてください。 お願いします。

  • 条件演算子論理ORについて

    すみません、独学でC言語を学んでいるものです。 どうかお助けいただけませんか? テキストに載っているものをそのままコンパイラしているのに エラーがでました。どこがいけないのか確認していただけませんか? *Memopad.cppで作ったプログラム* #include<stdio.h> void main(void) { int a,b,c; a=0; b=0; c=0; if((a=1)||(b=2)||(c=3)) { printf("%d\n",a); printf("%d\n",b); printf("%d\n",c); } } *エラーメッセージ* 添付を参照下さい。 うまくいくと 1 0 0 と表示されるようです。ずっと考えてもわからなかったのでお力を貸して頂ければ助かります。 よろしくお願いします。

  • またプログラムの修正おねがいします。

    キーボードから出力した3つの整数について、以下の判定を行い 判定結果を出力するプログラムを作成する。 全部同じ 2つ同じ バラバラ * 論理演算子を使わないこと。 * インデントを正しくつけること。 #include<stdio.h> void main(void) { int a,b,c; printf("a --->"); scanf("%d",$a); printf("b --->"); scanf("%d",$b); printf("c --->"); scanf("%d",$c); if(a==b){ if(b==c){ printf("全部同じ\n"); } } else if(a!=b){ if(b==c){ printf("2つ同じ"); } else if(a==c){ printf("2つ同じ"); } else{ printf("バラバラ"); } } } 最近c言語を習ったばっかりなのでわからないとこだらけです。よろしくお願いします。

専門家に質問してみよう