#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", a);
return 0;
}
0
!!が表示されます。そんなことがあるものか?理由は何ですか?
の振る舞いを研究するために意図的にステートメントに を入れ%d
ましprintf
たprintf
。
#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", a);
return 0;
}
0
!!が表示されます。そんなことがあるものか?理由は何ですか?
の振る舞いを研究するために意図的にステートメントに を入れ%d
ましprintf
たprintf
。
これ%d
は、 が期待しているint
が、float を提供したためです。
%e
/ %f
/を使用%g
して float を出力します。
0 が出力される理由について: 浮動小数点数はdouble
に送信する前に に変換されprintf
ます。リトル エンディアンで倍精度表現した数値 1234.5 は、
00 00 00 00 00 4A 93 40
A%d
は 32 ビット整数を消費するため、ゼロが出力されます。(テストとして、あなたはprintf("%d, %d\n", 1234.5f);
出力に乗ることができました0, 1083394560
。)
なぜfloat
が に変換されるのかというdouble
と、printf のプロトタイプが であるint printf(const char*, ...)
ため、6.5.2.2/7 から、
関数プロトタイプ宣言子の省略記号表記により、最後に宣言されたパラメーターの後で引数の型変換が停止します。デフォルトの引数昇格は、末尾の引数に対して実行されます。
そして6.5.2.2/6から、
呼び出された関数を示す式がプロトタイプを含まない型を持つ場合、各引数に対して整数昇格が実行され、型を持つ引数
float
は に昇格されdouble
ます。これらはデフォルト引数のプロモーションと呼ばれます。
(これを見つけてくれたAlokに感謝します。)
技術的に言えば はなく、 printf
各ライブラリは独自に実装しているため、自分printf
がしていることを実行して の動作を研究しようとする方法はあまり役に立ちません。システムでの の動作を調べようとしている可能性がありprintf
ます。その場合は、ドキュメントを読み、ソース コードをprintf
調べて、ライブラリで利用できるかどうかを確認してください。
たとえば、私の Macbook では、1606416304
あなたのプログラムで出力を取得します。
そうは言っても、float
a を可変引数関数に渡すと、float
は として渡されますdouble
。したがって、プログラムは として宣言a
したのと同じdouble
です。
a のバイトを調べるには、SO に関する最近の質問に対するこの回答double
を参照してください。
それをしましょう:
#include <stdio.h>
int main(void)
{
double a = 1234.5f;
unsigned char *p = (unsigned char *)&a;
size_t i;
printf("size of double: %zu, int: %zu\n", sizeof(double), sizeof(int));
for (i=0; i < sizeof a; ++i)
printf("%02x ", p[i]);
putchar('\n');
return 0;
}
上記のプログラムを実行すると、次のようになります。
size of double: 8, int: 4
00 00 00 00 00 4a 93 40
そのため、 の最初の 4 バイトdouble
が 0 であることが判明しました。これが0
、呼び出しの出力として得られた理由である可能性がありますprintf
。
より興味深い結果を得るために、プログラムを少し変更できます。
#include <stdio.h>
int main(void)
{
double a = 1234.5f;
int b = 42;
printf("%d %d\n", a, b);
return 0;
}
上記のプログラムを Macbook で実行すると、次のようになります。
42 1606416384
Linux マシンで同じプログラムを使用すると、次のようになります。
0 1083394560
%d
指定子はprintf
、整数を期待するように指示します。したがって、float の最初の 4 (プラットフォームによっては 2) バイトは整数として解釈されます。それらがたまたまゼロの場合、ゼロが出力されます
1234.5 のバイナリ表現は次のようになります。
1.00110100101 * 2^10 (exponent is decimal ...)
実際に IEEE754 double 値として表す C コンパイラを使用するfloat
と、バイトは次のようになります (間違いがなければ)
01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000
エンディアンがほとんどない (つまり、最下位バイトが最初に来る) Intel (x86) システムでは、このバイト シーケンスが逆になり、最初の 4 バイトがゼロになります。つまり、printf
出力されるものは...
IEEE754 に従った浮動小数点表現については、このウィキペディアの記事を参照してください。
未定義の動作を呼び出したためです。パラメーターの型について嘘をついて printf() メソッドの契約に違反したため、コンパイラーは好きなことを自由に行うことができます。プログラムに「dksjalk is a ninyhead!!!」という出力をさせることができます。技術的にはまだ正しいでしょう。
これは、フロートがバイナリで表現されているためです。整数に変換すると、0 のままになります。
その理由は、それprintf()
がかなりばかげた機能だからです。タイプはまったくチェックしません。最初の引数が であると言う場合int
(これは で言っていることです%d
)、それはあなたを信じて、 に必要なバイトだけを取りますint
。この場合、マシンが 4 バイトint
と 8バイトを使用すると仮定するとdouble
( はinside にfloat
変換されます)、 の最初の 4 バイトはゼロになり、これが出力されます。double
printf()
a
float を integer に自動的に変換しません。どちらもストレージの形式が異なるためです。したがって、変換する場合は (int) 型キャストを使用します。
#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", (int)a);
return 0;
}
C++ でタグ付けしたので、このコードはおそらく期待どおりに変換されます。
#include <iostream.h>
int main() {
float a = 1234.5f;
std::cout << a << " " << (int)a << "\n";
return 0;
}
出力:
1234.5 1234
関連するデータ型 (int、float、string など) で適切な書式指定子 (%d、%f、%s など) を使用するだけです。
何かを印刷する必要があったので、0 を印刷しました。C では 0 が他のすべてであることを思い出してください。
整数ではありません。を使ってみてください%f
。
%d ではなく %f が必要です