プロが教える店舗&オフィスのセキュリティ対策術

何度もすみません。C言語についてのご質問です。
以下のソースコードを作成してコマンドプロンプトで実行したところ、下の実行結果のようになりました。double型から無理やりint型にしたので値が変わってしまうのはわかりますが、今回の場合は少数切り捨てで「7」になるはずですよね?
ものすごい大きい値になってしまったんですけど、何故こうなってしまったのでしょうか?

[ソースコード]
#include <stdio.h>

int main(){

int a=2017;
double b=7.16;

printf("a=%d, b=%d\n", a, b);
printf("a=%d, b=%f\n", a, b);
printf("a=%f, b=%f\n", a, b);

return(0);
}

[実行結果]
a=2017, b=171798692
a=2017, b=7.160000
a=0.000000, b=7.160000

質問者からの補足コメント

  • ごめんなさい。bについてのご質問です。

      補足日時:2022/08/13 12:04
  • ご回答ありがとうございます。添付先のサイトを拝見しました。
    %7.2fや%6.3fみたいな形で少数型の数字の桁数の調整ができることは存じ上げておりますが、今回の場合、[7.16]と[171798692]は全く違う数だと思うんですよ。

    普通ならdouble型を無理やり整数型に直したら小数点以下が切り捨てで表示され、今回のbなら[7]となるはずだと思うのですが、何故全然違う値である[171798692]となってしまったのでしょうか?
    私の理解力不足でしたら申し訳ないです。

    No.1の回答に寄せられた補足コメントです。 補足日時:2022/08/13 12:23
  • うれしい

    理解できました。皆様、ご回答ありがとうございました。

      補足日時:2022/08/13 13:37

A 回答 (5件)

>普通ならdouble型を無理やり整数型に直したら小数点以下が切り捨てで表示され、今回のbなら[7]となるはずだと思うのですが、何故全然違う値である[171798692]となってしまったのでしょうか?



ptinfの関数の場合、そのままの値が引数として渡されます。
double b=7.16;
printf("b=%f\n",b);・・・①
printf("b=%d\n",b);・・・②
①の場合でも、②の場合でも、bの内容がそのままprintfに渡されます。
printfは①の場合、その値をdouble型のデータと解釈して、表示します。
printfは②場合、その値をint型のデータと解釈して、表示します。
そのために、171798692が表示されます。

以下のプログラムを実行すると、それが理解できるかと思います。
--------------------------------------------------------
#include <stdio.h>
#include <string.h>

int main(){

double b=7.16;
int z[2];
double c;
memcpy(z,&b,8);
memcpy(&c,z,8);
printf("b=%f\n",b);
printf("b=%d\n",b);
printf("z[0]=%d, z[1]=%d\n", z[0],z[1]);
printf("c=%f\n", c);

return(0);
}

--------------------------------
実行結果
b=7.160000
b=171798692
z[0]=171798692, z[1]=1075618775
c=7.160000
    • good
    • 1

printf("a=%d, b=%d\n", a, (int)b);


printf("a=%d, b=%f\n", a, b);
printf("a=%f, b=%f\n", (double)a, b);
こんな風に、書式に合わせて値が変換されることを期待しているかと思いますが、実際にはそうなりません。


printfのようなC言語の可変長引数(引数の数が固定ではない)関数には、ちょっとややこしい仕様があります。
その一つが、呼び出し側で指定した引数の型と数が、関数側に伝わらない、というのがあります。
printfでは、書式文字列中の%〜で、型と数を取得します。

printf("a=%d, b=%d\n", a, b);
を例にすると
int: 32ビット
double: 64bit として。

呼び出し側で
引数1: 文字列("a=%d, b=%d\n"へのポインタ
引数2: aの内容の32bitのビットパターン
引数3: bの内容の64bitのビットパターン
と設定してからprintfを実行します。

printf側では
引数1を解析して、
最初の%d

・引数2から
・書式がdだから 32ビット取ってきてintだと解釈する

次の%d

・引数3から
・書式がdだから 32ビット取ってきてintだと解釈する

と動作します。
あくまで、ビットパターンなので、2番目の%d に
(double) b /* 小数の切り捨て */
の変換が実行されるわけではなく、
*((int *) & b)
と同等のことが起っています。


printf("a=%f, b=%f\n", a, b);
では逆に、32ビットしかないaに32ビットのよくわからないビットを加えたものをdoubleと解釈したものです。
多分、0.000000(この後に0でない値が出てくる)と解釈されたのでしょう。


詳細は、環境によって違ってくる場合があります。
※ aの32ビット+bの先頭32ビット 、 bの後半32ビット+よくわからない32ビット となることもあるでしょう。


コンパイルスイッチで警告レベルを上げると、%と変数の型の違いを警告してくれることがあります。
ですが、基本的に「そこはプログラマがちゃんと合わせろ」というのがC言語の考え方です。
    • good
    • 1
この回答へのお礼

なるほどです。プログラミングしていくうえでは気を付けないといけないことなんですね。
大変参考になりました。ご回答、本当にありがとうございました。

お礼日時:2022/08/13 13:14

自分の質問に「ご質問」となるのは変なんですが…


まぁそれはさておき。

unsigned char *c=(unsigned char *)&b;
printf("b(HEX)=%02X%02X%02X%02X%02X%02X%02X%02X\n", *(c), *(c+1), *(c+2), *(c+3), *(c+4), *(c+5), *(c+6), *(c+7));
のようにして、
double bがメモリ上にどのように格納されているか見てみます。

b(HEX)=A4703D0AD7A31C40
となりました。(手元ですぐに確認できる環境がUbuntu 22.04の64Bitだった)

%dで扱う場合、ここから(おそらく32Bit)取ってくるので…
0xA4, 0x70, 0x3D, 0x0Aのバイト列をリトルエンディアンの数値として扱います。
0A3D70A4hになります。10進数表記に変換すると…171798692になりますね。
ということで、別におかしなことはないです。

「引数として渡すときにint型に変換される」のであれば、7になるでしょうけど、double型のままであれば上記の実験の通りでしょう。
    • good
    • 1

ごめんなさい、質問を読み違えてました。



(C++) | ブラウザでプログラミング・実行ができる「オンライン実行環境」| paiza.IO
https://paiza.io/projects/eUdqdut4TW4Gmg20a5_GxQ …

ココで試してみたところ、
printf("a=%d, b=%d\n", a, b);
は毎回違う結果を吐きました。マイナス値も。

知識のある方からの回答をお待ち下さい。失礼しました。
    • good
    • 1

この記事で解決。


  ↓
【C言語】小数点以下の桁数を指定する方法【printf】 | MaryCore
https://marycore.jp/prog/c-lang/format-floating- …
この回答への補足あり
    • good
    • 1

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!