• ベストアンサー

書式制御'%d'と'%f'について

以下のようなソースがあります。 実行すると、異常な、という処理において、 printf関数で、int型を'%f'で、処理すると 0.000000になります。 また、double型を'%d'で、処理すると 0になります。 これは何故、0になるのでしょうか。 説明できるかた、よろしくお願いいたします。 include <stdio.h> void main(){  int i;  double d;  i=100;  d=1.0;  printf("i=%d\n",i); /* 正常なprintf関数 */  printf("d=%f\n",d); /* 正常なprintf関数 */  printf("i=%f\n",i); /* 異常なprintf関数 */  printf("d=%d\n",d); /* 異常なprintf関数 */  return; }

  • burbe
  • お礼率35% (78/221)

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

  • ベストアンサー
  • ultraCS
  • ベストアンサー率44% (3956/8947)
回答No.3

printfというのは関数であり、ステートメントではありません。この意味を理解していますか。 関数呼び出し規約を一度読んでみればどういう事が起こっているのかわかるでしょう(理解できなければ、勉強してください)。たしか、C&Rに書いてあったはずです。 で、具体的な解説、面倒なので、intel86限定で説明します。  printf("i=%f\n",i); /* 異常なprintf関数 */ では、printfを呼び出す際、"i=%f\n"のアドレスと、iの中身(処理系にもよるが2バイトが多いかな)をスタックに積んで、Printfを呼び出します。で、printfがどう動作するかというと、まず、書式のアドレスをスタックから取り出し、書式の解析を始め、それに従って、粛々と処理します。%fが出てきた時点で、スタックからfloatに相当する4バイトを取り出そうとします、しかし、ここでは、スタックには2バイトしか残っていません、で、ここはOSにもよりますが、stack emptyでエラーになるか、stack boundaryを破壊して不定の値を取り出すかになります。 floatにintを突っ込めば、ntの値が256以下なら、実質的には表示としてはfloatingのフォーマットにも寄りますがexponentが0(あるいは2~-128)ですから、0.0000になるのはわからない話ではありません。 また、doubleをintで処理した場合、2バイトだけ取り出してくるのですが、エンディアンにもよりますが、0になることはあり得ます。 いずれにしろ、これはプログラマとしてはやってはいけないことの一つです。理解するのは大切ですが、考えるだけ無駄でしょう。

参考URL:
w@
burbe
質問者

お礼

ありがとうございました。

その他の回答 (3)

  • ency
  • ベストアンサー率39% (93/238)
回答No.4

期待した動作をさせたいのなら、次のようにすればよいでしょう。 (修正前) printf("i=%f\n",i); /* 異常なprintf関数 */ printf("d=%d\n",d); /* 異常なprintf関数 */ ⇒ (修正後) printf( "i=%f\n", (double)i ); printf( "d=%d\n", (int)d ); 「未定義動作」はそもそも動作が保障されていないわけですから、たとえどのようなことが起こっても文句は言えない、ということです。 # printf() の double型の書式に "%lf" を使用するのも未定義だったような気がするけど、 # 結構よく見かけるんですよね。 # 明らかに scanf() の書式との混同なんだけど、たいていの処理系でそれなりに # 動いてしまうみたいですから。。。

burbe
質問者

お礼

ありがとうございました。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.2

「JIS X3010:2003の7.19.6.1 fprintf関数」では、 実引数の型が対応する変換指定に対して,正しくない場合,未定義とする. となっています。printfの場合も同様ですので、やはり未定義です。すなわち、そんな間違った使い方をした場合のことは何一つケアしていないので、成り行きに任せた結果が、たまたま > printf関数で、int型を'%f'で、処理すると > 0.000000になります。 > また、double型を'%d'で、処理すると > 0になります。 のようになっただけです。

burbe
質問者

お礼

ありがとうございました。

  • phoenix343
  • ベストアンサー率15% (296/1946)
回答No.1

整数型と浮動小数型の違いですね。double型は上位何ビットかを、指数にあてていて、そのまま変換はできません。 通常の代入文ではコンパイルするときに型変換してくれますが、printf文の引数として渡すとそうはいかず整数型のまま浮動小数型に入れるので違う値になります。 こんな説明で、分かるかな?

burbe
質問者

お礼

ありがとうございました。

関連するQ&A

  • printf()で出力したいんですが?

    #include <stdio.h> int main( void ) { int air[4]; int i; i = 0; printf( "データ入力\n" ); do{ scanf( "%d\t", &air[ i++ ]); } while(air[ i - 1 ] > 0 ); printf( "%d\n", i ); return 0; } このプログラムはコンパイル出来ますが、期待していたprintf関数での出力ができません。 その理由として何がありますか?関数の使い方が間違っているのか、DO~WHILE文が使えない場所 なのか、もし直すとすればどう直せばいいのですか。

  • プログラムの添削

    以下のような数当てゲームを作りました.なるべくうまいプログラムを書けるようになりたいのですが,どのような改善点がありますか?よろしくお願いします. /*数当てゲームを作りなさい.*/ #include<stdio.h> void maegaki(void); /*このように関数を定義しまくることに意味はあるのか?main関数はすっきりするけど.*/ void in_check_out(int i); int main(void) { int i; int j; maegaki(); for(j=0;j<10;j++) { scanf("%d",&i); in_check_out(i); if(!(i-1)) return 0; printf("残り%d回です.\n",9-j); } return 0; } void maegaki(void) { printf("数当てゲームをはじめます.\nぼくの好きな整数を当ててください.\nチャンスは10回です.\nヒントはボゾン\n"); } void in_check_out(int i) { if(!(i-1)) { printf("正解!答えは1です.\n"); } else { printf("残念!\n"); if(i>1) printf("%dより小さいです.\n",i); else printf("%dより大きいです.\n",i); } }

  • フィボナッチ数列

    C言語の問題です。 フィボナッチ数列について。 以下のプログラムはある整数を入力し、その整数以下のもっとも大きなフィボナッチ数列に含まれる整数を返すものである。 #include <stdio.h> int main(void){ int limit,new=1,pre=0,work result; scanf("%d", &limit); while(new<=limit){ /*printf("%d %d\n,pre,new);トレース用*/ work=new; new=work+pre; pre=work; } result =pre; printf("%d\n", result); } 以下を参考に、このプログラムの一部を関数にして書き換えなさい。 #include <stdio.h> /*ここに関数を記述*/ int main(void){ int limit,result; scanf("%d",&limit); result = fibnum(limit); printf("%d\n", result); } という問題です。 私は以下のように考えたのですが… #include <stdio.h> void fibnum(int limit){ int f0, f1; f0 = 0; f1 = 1; printf("%d\n", f0); printf("%d\n", f1); while(1){ f0 += f1; if (f0 > limit)return; printf("%d\n", f0); f1 += f0; if (f1 > limit)return; printf("%d\n", f1); } } int main(void){ int limit, result; scanf("%d",&limit); result = fibnum(limit); printf("%d\n", result); return result; } 実行すると 「S:\デスクトップ\v\v.cpp(30) : error C2440: '=' : 'void' から 'int' に変換することはできません。(新しい動作 ; ヘルプを参照) void 型の式は他の型へ変換できません。 cl.exe の実行エラー」 と出てきてしまいます。 考えてみたのですが、エラーの理由がよくわかりません。 どこが間違っているのかなど、ご指摘いただければ嬉しいです。

  • 関数におけるif文とreturn文について

    ◎1-------------------------------------------- #include<stdio.h> #include<math.h> double maxdt(double a,double b); void disp_sqrt(double n); int main(void) { double mx; mx=maxdt(22.33,44.55); printf("mx=%f\n",mx); disp_sqrt(3.0); disp_sqrt(-6.0); return 0; } double maxdt(double a,double b) { if(a>b) return a; else return b; } void disp_sqrt(double n) { if(n<=0.0) return; printf("%f の平方根=%f\n",n,sqrt(n)); } ----------------------------------------------- ◎2------------------------------------------- #include<stdio.h> #include<math.h> double maxdt(double a,double b); void disp_sqrt(double n); int main(void) { double mx; mx=maxdt(22.33,44.55); printf("mx=%f\n",mx); disp_sqrt(3.0); disp_sqrt(-6.0); return 0; } double maxdt(double a,double b) { if(a>b) return a; else return b; } void disp_sqrt(double n) { if(n<=0.0){ return; printf("%f の平方根=%f\n",n,sqrt(n)); } } ----------------------------------------------- ◎3-------------------------------------------- #include<stdio.h> #include<math.h> double maxdt(double a,double b); void disp_sqrt(double n); int main(void) { double mx; mx=maxdt(22.33,44.55); printf("mx=%f\n",mx); disp_sqrt(3.0); disp_sqrt(-6.0); return 0; } double maxdt(double a,double b) { if(a>b) return a; else return b; } void disp_sqrt(double n) { if(n<=0.0){ return; } else{ printf("%f の平方根=%f\n",n,sqrt(n)); } } -------------------------------------------------- ◎1は参考書を参考に作ったものです。 ◎1は正常に動きます。 以上3つのプログラムで、疑問に思ったのは、関数「void disp_sqrt(double n);」についてなのですが、自分はif文が文が1つでもカッコ{ }を付けたい考えなので、◎1の「void disp_sqrt(double n)」の関数のif文に{}を付けようと思い、まず◎2のように変えたところ、平方根の表示が何も出ませんでした。 return文も文の1つだと考え、◎3のような形は正常に動きました。 return文とprintf文の2つの文があるという考えは間違っているのでしょうか? 後、◎1は何故{ }が無くてもよく、◎2は何も表示されないのでしょうか? 教えていただけると嬉しいです。

  • C++の打切り誤差についてお聞きしたいのですが・・

    ↓のプログラムが、なぜ実行結果のsumが1ではなく1.000054や9.99999999999906e-001、9.9999999999990619e-001といった答えにになってしまうのでしょうか? 10000のところを512でやった場合は普通に1と出力されたのに・・・ #include <stdio.h> int main(void){ int i; float f; double d; f = 0.0; for(i = 0; i<10000; i++) { f += 1.0/10000; } printf("float: n=%d sum=%f\n", 10000, f); d = 0.0; for(i = 0; i<10000; i++) { d += 1.0/10000; }// end for printf("double:16.14e: n=%d sum=%16.14e\n", 10000, d); printf("double:18.16e: n=%d sum=%18.16e\n", 10000, d); return 0; } 実行結果 float: n=10000 sum=1.000054 double:16.14e: n=10000 sum=9.99999999999906e-001 double:18.16e: n=10000 sum=9.9999999999990619e-001 続行するには何かキーを押してください . . .

  • C言語

    以下の出力結果はどうなりますか? 教えてください。 #include <stdio.h> #include <stdib.h> int main (void){ int n,f; n=792; f=2; while(f*f<=n){ while(n%f==0){ printf("%d",f); n=n/f; } f++; } if(n !=1) printf("%d",n); printf("\n"); return 0; }

  • C言語

    #include <stdio.h> #include <stdib.h> int main (void){ double a[5]={0.0,4.0,0.0,-5.0,1.0}; double x; int i,j,k,n; n=4; x=0.75; for(i=1;i<=n;i++) printf("%10.5f ,",a[i]); printf("\n"); for (i=1; i<=n+1; i++) printf("----------") printf("\n"); while(n>=1){ for(i=1; i<=n; i++) a[i]=a[i-1]*x+a[i]; for(i=1; i<=n; i++) prontf("%10.5f ,"a[i]); printf("\n"); n=n-1; } return 0; }

  • C言語の参照はずしについて

    ソートのプログラムなんですが #include <stdio.h> #include <stdlib.h> int comp(const void *, const void *); int main() { int i; int test[6] = {10, 8, 2, 6, 4, 0}; qsort(test, (size_t)6, sizeof(int), comp); printf("\n"); for (i = 0; i < 6; i++) printf("%d\n", test[i]); return 0; } int comp(const void *a, const void *b) { static int i = 1; printf("%02d--%d,%d\n", i, *(int *)a, *(int *)b); i++; return (*(int *)a - *(int *)b); } 最後のreturnの()の中身がよくわかりません。「参照はずし」という事をしてるらしいんですが「参照はずし」とは何ですか意味も教えてください。

  • 数値の連続入力終了条件について

    C言語初心者です。よろしくお願いします。 早速質問なのですが、while文を使ったscanf()関数による数値連続入力で、 ◎1---------------------------------------------- #include<stdio.h> int main(void) {      double dt,sum=0.0;      while(scanf("%lf",&dt) !=EOF){   sum=sum+dt; }   printf("合計=%f\n",sum); return 0; } ---------------------------------------------- ◎1のようにすれば、Ctrl+ZでEOFが返されたら終了とわかるのですが、今度は「0」が入力されたら処理を終了するというプログラムで、 ◎2---------------------------------------------- #include<stdio.h> int main(void) {      double dt,sum=0.0;      while(scanf("%lf",&dt) !=0.0){   sum=sum+dt; }   printf("合計=%f\n",sum); return 0; } ---------------------------------------------- ◎2のようにすると「0」が入力されても、終了せず、以下に示す◎3のように、しないと終了出来ません。 ◎3---------------------------------------------- #include<stdio.h> int main(void) {      double dt,sum=0.0;          scanf("%lf",&dt);      while(dt!=0.0){   sum=sum+dt; scanf("%lf",&dt); }   printf("合計=%f\n",sum); return 0; } ---------------------------------------------- ◎2で何故、◎1のように出来ず、◎3のようなscanf()を1回目、2回目と判定を入れなければならないか教えて下さい。

  • コンパイルエラー

    コンパイルしても、12行目(printf("%d番目の整数を入力してください。"i+1);が、関数呼び出しに)がないとでます。でもどこが間違っているか分かりません。 教えてください。お願いします。 #include <stdio.h> int main(void) { int num; int sum=0; int i; for(i=0; i<10; i++){ printf("%d番目の整数を入力してください。"i+1); scanf("%d", &num); sum+=num; } printf("合計は、%dです。\n", sum); return(0); }

専門家に質問してみよう