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

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

ただいまの
回答率

90.51%

  • C

    4534questions

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

Cで DBL_MAX - 1.0 の結果がDBL_MAXになってしまう

解決済

回答 3

投稿

  • 評価
  • クリップ 1
  • VIEW 715

yama_da

score 65

Cで DBL_MAX-1.0 の値が欲しかったのですが、実際に実行してみると結果の値は DBL_MAX のままでした。
気になって下のコードで実験してみました。

#include <stdio.h>
#include <limits.h>
#include <float.h>

int main(void) {

  printf("\n");

  double dm=DBL_MAX;
  double r1,r2,r3,r4,r5;

  r1= dm - 1.0 - dm;
  r2= dm - 123.0 - dm;
  r3= dm - 12345.0 - dm;
  r4= dm - 1234567.0 - dm;
  r5= dm - 123456789.0 - dm;

  printf("r1 = %.5lf\n",r1);
  printf("r2 = %.5lf\n",r2);
  printf("r3 = %.5lf\n",r3);
  printf("r4 = %.5lf\n",r4);
  printf("r5 = %.5lf\n\n",r5);




  float fm=FLT_MAX;
  float s1,s2,s3,s4,s5;

  s1= fm - 1.0 - fm;
  s2= fm - 123.0 - fm;
  s3= fm - 12345.0 - fm;
  s4= fm - 1234567.0 - fm;
  s5= fm - 123456789.0 - fm;

  printf("s1 = %.5f\n",s1);
  printf("s2 = %.5f\n",s2);
  printf("s3 = %.5f\n",s3);
  printf("s4 = %.5f\n",s4);
  printf("s5 = %.5f\n\n",s5);




  int im=INT_MAX;
  int t1,t2,t3,t4,t5;

  t1= im - 1.0 - im;
  t2= im - 123.0 - im;
  t3= im - 12345.0 - im;
  t4= im - 1234567.0 - im;
  t5= im - 123456789.0 - im;

  printf("t1 = %d\n",t1);
  printf("t2 = %d\n",t2);
  printf("t3 = %d\n",t3);
  printf("t4 = %d\n",t4);
  printf("t5 = %d\n\n",t5);

  return 0;
}

ですが、結果は

r1 = 0.00000
r2 = 0.00000
r3 = 0.00000
r4 = 0.00000
r5 = 0.00000

s1 = 0.00000
s2 = 0.00000
s3 = 0.00000
s4 = 0.00000
s5 = 0.00000

t1 = -1
t2 = -123
t3 = -12345
t4 = -1234567
t5 = -123456789

となり、INT_MAXのとき以外は全く期待した値になってくれませんでした。なぜでしょうか?

コンパイラ:gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04) 
エディタ :Emacs 24.3.1

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

checkベストアンサー

+1

こんにちは。

単純に桁落ちです。
doubleの指数部は11ビットで、仮数部が52ビットです。
DBL_MAXは 1.11111...(52ビット) * 2^1023 です。
小数点直下から52ビット連続の1を1023ビット左シフトした値なので、結果は小数点の上に1023-52=971ビットの0が続きます。
その値から、高々123456789.0を引いても、小数点の上971ビット目は1ですので切り上られて元に戻ってしまいます。

小さな10進数の値で考えると分かりやすいです。
50,000から100引くと49,900となります。この1,000の桁を四捨五入したら50,000に戻るという現象です。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/07/11 11:53

    1. 111...(52bit)....11*2^1023
    を1023bit左シフトすると
    1111111.....(53bit).....11110000000.....(971bit).......00000.0
    になって、このでかい値から123456789.0の2進数(何桁になるかは分からないけど)の値を引いたところで結果はたいして変わらないから、971bit目の値で切り上げられて元に戻る

    上の解釈で合っていますか?違っていたら訂正お願いします。
    あと、小数点の上の971bit目で切り上げられるのは、doubleの仮数部が52bitで小数点以下52桁までしか表せないからですか?

    キャンセル

  • 2016/07/11 11:57

    > 上の解釈で合っていますか?

    あってますよ。

    > あと、小数点の上の971bit目で切り上げられるのは、doubleの仮数部が52bitで小数点以下52桁までしか表せないからですか?

    考え方はその通りです。

    キャンセル

  • 2016/07/11 12:06

    返信ありがとうございます。
    もうひとつ質問なのですが、そうする必要があるのかどうかは抜きにして、DBL_MAXから1引いた値を得ることは不可能ということでしょうか?

    キャンセル

  • 2016/07/11 19:33

    (いくぶん重箱の隅つつきなコメントですが)このケースは「桁落ち」ではなく、「情報落ち」が起きています。

    https://ja.wikipedia.org/wiki/%E8%AA%A4%E5%B7%AE#.E6.83.85.E5.A0.B1.E8.90.BD.E3.81.A1

    キャンセル

  • 2016/07/11 19:40

    yohhoyさん。

    なるほど。フォローありがとうございます。

    キャンセル

  • 2016/07/13 12:54 編集

    色々ありがとうございました。
    最初の問題は別の方法で解決出来ました。
    また機会があればよろしくお願いします。
    yohhoyさんもありがとうございました。

    キャンセル

+1

DBL_MAXはだいたい1.7976931348623157e+308のように定義されています。これは、1.7976……×10の308乗のことで、全て数字で表すと309桁必要になります。

その値から1を引くというのはどういうことかというと、桁位置を合わせないといけませんから、1をe+308で表して、0.000……001e+308(0が308個)を引くということになります(実際には同様のことを2進数で処理しています)。しかしながら、doubleの精度はせいぜい10数桁程度なので、その桁数を遙かに上回る数の0が続くと、それは0とみなされてしまいます。
結果、1を引いたつもりでも実際には0を引いているのと同じになるのです。123456789でも0の数が308個から300個に減った程度で結果は同じです。

doubleに限らず、コンピューターで数値を扱う場合、精度(有効桁数)や内部的な仕組み(2進数表現)などを考慮しないと、思わぬ結果になることがままあります。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/07/13 12:53

    回答ありがとうございます。
    最初の問題は別の方法で解決出来ました。
    また機会があればよろしくお願いします。

    キャンセル

+1

INT_MAXのとき以外は全く期待した値になってくれませんでした。なぜでしょうか? 

端的に言えば、そういう仕様です。

floatdoubleなどの「浮動小数点数(型)」と、intなどの「整数(型)」の仕組みを理解する必要があります。それぞれの特徴だけを説明するなら、下記の通りです:

  • 浮動小数点数:実数値(整数や小数点以下を含む値)を 近似的に 表現できます。代わりに、絶対値が大きい値の場合は、その有効桁数が制限されます。
  • 整数:整数値を 厳密に 表現できます。代わりに、あまり広い値域を表現することはできません。

(コメントより) そうする必要があるのかどうかは抜きにして、DBL_MAXから1引いた値を得ることは不可能ということでしょうか?

数学的な意味で「DBL_MAXから1引いた値」は、double型として表現できません(存在しません)。最も近い値として DBL_MAX - 1.0 == DBL_MAX となってしまいます。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/07/13 12:51

    回答ありがとうございます。
    最初の問題は別の方法で解決出来ました。
    また機会があればよろしくお願いします。

    キャンセル

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

  • C

    4534questions

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