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

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

ただいまの
回答率

87.90%

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

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,647

score 77

 前提・実現したいこと

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
となり整数部がうまくいってない上に繰り上がりもできていない、、、

 該当のソースコード

#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 s_int, t_int; //整数部
  double s_dec, t_dec, q; //少数部

  q = ( 1 + sqrt(5))/2; //黄金比

//整数部と少数部の分離
  s_int = x.a + (int)(x.b * q);//少数部は切り捨てだからこれで良いと思う
  t_int = y.a + (int)(y.b * q);
  s_dec = x.b * q - (int)(x.b * q);
  t_dec = y.b * q - (int)(y.b * q);

//デバック用
printf("少数部変更前s_int=%lld t_int=%lld s_dec=%f t_dec=%f\n",s_int ,t_int,s_dec,t_dec);

  //少数部がマイナスだと結果に支障をきたすためその分両方に足して正にする        
  if(s_dec < t_dec) {//2つの少数部のうち小さい方を両方に足せば良い

    if(s_dec < 0.) { //小さい方で、かつ足すのは影響が出る負の場合
      t_dec += fabs(s_dec);
      s_dec += fabs(s_dec);

      if(t_dec >= 1.) {//負の小数点の絶対値を足したことによって(少数部>1.)となった時、繰り上げ                                               
        t_int++;
        t_dec -= 1;
      }
    }
  } else if(t_dec < 0.) { //同様
    s_dec += fabs(t_dec);
    t_dec += fabs(t_dec);

    if(s_dec >= 1.) {//繰り上げ                                                 
      s_int++;
      s_dec -= 1;
    }
  }

//デバック用
 printf("少数部変更かつ繰り上げ後s_int=%lld t_int=%lld s_dec=%f t_dec=%f\n",s_int,t_int,s_dec,t_dec); 

  if(s_int == t_int) { //整数部が等しかったら少数部で比較                       
    if(s_dec == t_dec){
      return 0;
    } else if(s_dec > t_dec) {
      return 1;
    } else {
      return -1;
    }
   }

  if(s_int > t_int){ 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;
}

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

Unix gccコンパイラ

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+1

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/20 19:51

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

    キャンセル

0

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/19 17:06

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

    キャンセル

  • 2018/10/19 17:11

    https://dobon.net/vb/dotnet/beginner/floatingpointerror.html
    とか、
    「浮動小数点数 誤差」とかでぐぐるとたくさん出てきますねー

    キャンセル

  • 2018/10/19 17:24

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

    キャンセル

0

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

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

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

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

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

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

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

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

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


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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

check解決した方法

-2

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;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 87.90%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

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