質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

4回答

3664閲覧

[C言語]long long int型において小数点を含む数値比較でオーバーフロー?して正しい結果が得られません。

apeirogon0813

総合スコア117

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2018/10/19 07:55

編集2018/10/19 08:21

前提・実現したいこと

C言語において黄金比
φ = (1 + √5) / 2 
の値を用いて、標準入力から a + b φ (a, b は整数) の形の 2 つの数に対し,1つめの方が大きければ 1 を,2 つめの方が大きければ -1 を,どちらも等しければ 0 を標準出力に出力するプログラムを作成するにおいて、
入力a ,bの整数をlong long int型で絶対値は10^10以下で考えて良いという問題なのですが、
比の値が小数点を含んでおり、整数部と少数部に切り離し、例1のような負の少数部の影響で結果に影響が出ないように小さい負の方の少数を両方に絶対値で足してあげ、繰り上がった分は整数部に足してあげるという方法で数値比較を行ったのですが、**大きい入力を与えた時オーバーフローしているのか、整数部が正しく表示されておらずと正しい結果が出力されません。**改善のご教示願います。

###例1
入力         
5 -3    
-3 2

としたとき

s_int = 1, t_int = 0, s_dec = -0.854102, t_dec = 0.236068

となり大きい方の整数部から比較し、判定していくと 

s > t

となり1が出力されますが
実際は小数部も足し合わせると
s<tでー1が正しい出力
となるので
s_decの絶対値をs_dec, t_decに加算し繰り上がった場合も整数部に加算してあげた。

###入出力例
入力例1      出力
5 -3        -1
-3 2

入力例2      出力
3 -1 1
-2 2

入力例3      出力
3 -2 0
3 -2

入力例4      出力
1392 -1209 1
-2789 1375

入力例5           出力
51630685 -25993565    1
-113949456 76340590

入力例6           出力
495915634 -498021163  -1
-1340396269 636882007

入力例4くらいの桁まではうまくいくのですが入力例5くらいの桁になってくるとと
s_int=9572214 t_int=9572213 s_dec = 0.000000 t_dec = 1.000000
となり整数部がうまくいってない上に繰り上がりもできていない、、、

該当のソースコード

C

1#include<stdio.h> 2#include<math.h> 3 4struct golden { 5 long long int a; 6 long long int b; 7}; 8 9 10int compare(struct golden x, struct golden y) { 11 long long int s_int, t_int; //整数部 12 double s_dec, t_dec, q; //少数部 13 14 q = ( 1 + sqrt(5))/2; //黄金比 15 16//整数部と少数部の分離 17 s_int = x.a + (int)(x.b * q);//少数部は切り捨てだからこれで良いと思う 18 t_int = y.a + (int)(y.b * q); 19 s_dec = x.b * q - (int)(x.b * q); 20 t_dec = y.b * q - (int)(y.b * q); 21 22//デバック用 23printf("少数部変更前s_int=%lld t_int=%lld s_dec=%f t_dec=%f\n",s_int ,t_int,s_dec,t_dec); 24 25 //少数部がマイナスだと結果に支障をきたすためその分両方に足して正にする 26 if(s_dec < t_dec) {//2つの少数部のうち小さい方を両方に足せば良い 27 28 if(s_dec < 0.) { //小さい方で、かつ足すのは影響が出る負の場合 29 t_dec += fabs(s_dec); 30 s_dec += fabs(s_dec); 31 32 if(t_dec >= 1.) {//負の小数点の絶対値を足したことによって(少数部>1.)となった時、繰り上げ 33 t_int++; 34 t_dec -= 1; 35 } 36 } 37 } else if(t_dec < 0.) { //同様 38 s_dec += fabs(t_dec); 39 t_dec += fabs(t_dec); 40 41 if(s_dec >= 1.) {//繰り上げ 42 s_int++; 43 s_dec -= 1; 44 } 45 } 46 47//デバック用 48 printf("少数部変更かつ繰り上げ後s_int=%lld t_int=%lld s_dec=%f t_dec=%f\n",s_int,t_int,s_dec,t_dec); 49 50 if(s_int == t_int) { //整数部が等しかったら少数部で比較 51 if(s_dec == t_dec){ 52 return 0; 53 } else if(s_dec > t_dec) { 54 return 1; 55 } else { 56 return -1; 57 } 58 } 59 60 if(s_int > t_int){ return 1;} //整数部が等しくなかったら 61 else { return -1; } 62} 63 64int main(void) { 65 struct golden x, y; 66 int n; 67 scanf("%lld %lld",&x.a, &x.b); 68 scanf("%lld %lld",&y.a, &y.b); 69 n = compare(x, y); 70 printf("%d\n",n); 71 return 0; 72} 73 74

補足情報(FW/ツールのバージョンなど)

Unix gccコンパイラ

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ちょっと理解し切れてないところもあるのですが、0へ向かって丸める(int)のキャストじゃなくて、負の無限大に向かって丸めるfloor()の関数を使ってはどうでしょうか?
そうすると小数部は常にプラスです。

投稿2018/10/19 10:01

otn

総合スコア84423

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

apeirogon0813

2018/10/20 10:51

なるほど!負の場合の誤差の考慮をできていませんでした
guest

0

自己解決

#include<stdio.h>
#include<math.h>

struct golden {
long long int a;
long long int b;
};

int compare(struct golden x, struct golden y) {
long long int A, B, Aabs, Babs, V, W;

//printf("%f\n",q);
A = x.a - y.a;
B = x.b - y.b;
Aabs = abs(A);
Babs = abs(B);

V = Babs * sqrtl(5);
W = 2 * Aabs - Babs;

//printf("%lld\n",V);

if(A == 0 && B == 0) { return 0; }

if(A > 0 && B > 0) { return 1; }
if(A < 0 && B < 0) { return -1; }

if(B == 0) {
if(A > 0) { return 1; }
else { return -1; } //A=0なら前の条件でreturnされているためelse
}
if(A == 0) {
if(B > 0) {return 1; }
else { return -1; }
}

if(A > 0 && B < 0) {
if(W <= V) { return -1;}
else { return 1; }
}
if(A < 0 && B > 0) {
if(W <= V) { return 1;}
else { return -1; }
}
}

int main(void) {
struct golden x, y;
int n;
scanf("%lld %lld",&x.a, &x.b);
scanf("%lld %lld",&y.a, &y.b);
n = compare(x, y);
printf("%d\n",n);
return 0;
}

投稿2018/10/23 02:02

apeirogon0813

総合スコア117

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

多分、求める結果にはならないと思いますが、、、、

整数部が一致しない件は、otnさんが書かれたように、(int)のキャストが原因。
5桁とかが問題で無く、bの値が xと yで符号が異なるからです。ここは、floor()関数で解決すると思います。

C

1s_int = x.a + floor(x.b * q); // 小数部は切り捨てだからこれで良いと思う 2t_int = y.a + floor(y.b * q);

こちらでも同じですが、、

C

1s_int = (int)(x.a + x.b * q); // 小数部は切り捨てだからこれで良いと思う 2t_int = (int)(y.a + y.b * q);

次に問題となるのは、小数部の比較。
浮動小数点(double)で、 == 一致するのは、特殊な場合のみ。同じ数値の組合せで無い場合、一致するのは、稀と思います。一般には、一定の値未満の場合、同じと見なすべきと思いますが、この扱いがもう一つ。

現在の方法では、double で計算しても同じなので、もう一つしっくりこないです。
回答依頼がありましたので、一応。。。

[追記]
小数部の比較ですが、

C

1int x_dec = (int)(((x.b * q) - floor(x.b * q)) * q + 0.1);

とすれば、x の小数部に対応する整数が取得できると思うので、y についても同様に求め、比較すれば、良いと思うのですが、どうでしょう。 (時間が取れれば、確認予定)
なお、式内の +0.1 は、浮動小数の誤差対策です。

投稿2018/10/20 09:48

編集2018/10/20 14:18
pepperleaf

総合スコア6383

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

まず、PCが扱う浮動小数点数というのは2進数の数値となります
そして、あなたが扱う数値は10進数の数値となります
その10進数と2進数の数値の違いが、いわゆる誤差、となり、あなたのいう正しく出力されない結果となってしまいます

ということで、そこらへん調べてみましょう

#あなたのいう0.1は、浮動小数点数では表現できません

投稿2018/10/19 08:04

y_waiwai

総合スコア87719

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

apeirogon0813

2018/10/19 08:06

少し例をだしておしえていただけませんか
apeirogon0813

2018/10/19 08:24

なるほど、参考にします。 今回の小数点の有効桁くらいはdoubleでカバーできるので、大きい整数部がなぜ正しい値にならないのかご教示願いたいです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問