<要求事項>
分数は、 例)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になると表示できなかったり、桁が大きい数で計算しようとすると値がおかしくなってしまいます。
どなたかプログラム改良にご助力願えないでしょうか?
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」が入ることになります。
注:あくまで概念の説明です。
回答ありがとうございます。オーバーフローの概念を理解することができ、課題もなんとか終わらせることができました。分かりやすい説明ありがとうございました。
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 );
}
注:インデントに全角空白を用いています。タブに一括変換して下さい。
回答ありがとうございます。課題のほうですが、何とか終わらせることができましたので、お書き頂いたプログラムはこれからの学習に役立てていきたいとます。
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
No.3
- 回答日時:
「桁が大きいと結果がおかしい」というのは, 多分オーバーフローですね. ことあるごとに gcd を使って約分していくしかないと思います.
例えば a/b + c/d を計算するのに, (ad + bc) / (bd) としてしまってはダメで, 分母を最初から b と d の最小公倍数にする必要があります. とはいっても, これだけで本当に十分とは思えないんですが....
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));
とか(元の計算式があってるかは確認していません)
No.1
- 回答日時:
気になる点
・ ユークリッド互除法gcdの入力の制約を満たしていない
xがy以上.x,yは自然数の制約.
引数の0であるのにgcdを呼ぶ場合があるのでは?
・符号のチェックが少しわかりにくい
sign = (a*d + b*c) / (b*d);
sign = sign / abs(sign);
こうしたほうがあとから見てわかりやすいのでは?
・冗長な部分が多い
分数の入力,計算結果の表示は関数にできますね.
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C 言語の Gauss Jordan 法について 2 2022/12/28 11:16
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# C言語 プログラミング 4 2022/05/22 11:53
- C言語・C++・C# 至急教えてください。プログラミングの問題です。 最初に正の整数nの入力を受け付け、次に分数の分子と分 1 2022/07/19 17:03
- C言語・C++・C# 至急お願いします。プログラミングの問題です。 最初に正の整数nの入力を受け付け、次に分数の分子と分母 3 2022/07/19 17:09
- C言語・C++・C# 至急教えてください。プログラミングの問題です。 malloc関数を使ってください!お願いします! 最 1 2022/07/21 09:28
- C言語・C++・C# プログラミング c言語 4 2023/03/07 01:05
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- C言語・C++・C# C言語 3 2022/10/04 15:07
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
このQ&Aを見た人はこんなQ&Aも見ています
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
4の倍数を論理演算で表す。。
-
10個出力で改行したいのですが...
-
小数点切捨て表示
-
コーディング
-
scanfに文字が入力されたときに...
-
コマンドラインに出力した文字...
-
【C言語教えてください】sin波...
-
printf で二進表示を行いたい。
-
strcmp
-
C言語のじゃんけんゲームのプロ...
-
2の累乗を計算するプログラム...
-
srand(time(NULL))の使い方
-
c言語 プログラム ピラミッド
-
Cプログラムについて
-
円の面積を求めるプログラミン...
-
cshの文字列操作(0埋め)
-
defineで定数が置き換えられな...
-
unsigned int型について
-
パスカルの三角形についてのCプ...
-
Cでオセロゲームプログラム
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
10個出力で改行したいのですが...
-
既約分数の表示プログラム
-
printf で二進表示を行いたい。
-
8人分のテストの点数を入力し、...
-
printf( " %2d", p * q );
-
strcmp
-
CTRL+Dでループを抜けるには
-
4の倍数を論理演算で表す。。
-
%P と %X の違い
-
【C言語教えてください】sin波...
-
c言語でAからZまでを表示する...
-
cshの文字列操作(0埋め)
-
万年カレンダーのC言語プログラ...
-
コマンドラインに出力した文字...
-
scanfに文字が入力されたときに...
-
コンパイルエラーについて
-
ホームページをC言語で作りたい...
-
改行について 1行に何個かづ...
-
台形の面積を求めるプログラム
-
なぜgccはstdio.hをインクルー...
おすすめ情報