#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", a);
return 0;
}
0
を表示します!!そんなことがあるものか?理由は何ですか?
printf
の動作を調査するためにprintf
ステートメントに%d
を意図的に配置しました。
これは、_%d
_がint
を予期しているが、フロートを指定しているためです。
_%e
_/_%f
_/_%g
_を使用してフロートを印刷します。
0が出力される理由:浮動小数点数はdouble
に送信される前にprintf
に変換されます。リトルエンディアンの二重表現の数値1234.5は
_00 00 00 00 00 4A 93 40
_
_%d
_は32ビット整数を消費するため、ゼロが出力されます。 (テストとして、printf("%d, %d\n", 1234.5f);
出力_0, 1083394560
_を取得できます。)
Printfのプロトタイプがint printf(const char*, ...)
であるため、float
がdouble
に変換される理由については、6.5.2.2/7から
関数プロトタイプ宣言子の省略記号により、最後に宣言されたパラメーターの後で引数型の変換が停止します。 デフォルトの引数の昇格は末尾の引数で実行されます。
および6.5.2.2/6から、
呼び出された関数を表す式の型がプロトタイプを含まない場合、整数の昇格は各引数で実行されますおよび型
float
の引数はdouble
に昇格されます。これらは(デフォルトの引数プロモーション。
(これを見つけてくれてありがとうAlok。)
技術的に言えば、theprintf
はなく、各ライブラリは独自のライブラリを実装しているため、printf
あなたがしていることをすることによる行動は、あまり役に立たないでしょう。システムでのprintf
の動作を調べようとしている可能性があります。その場合は、ドキュメントを読み、ライブラリで使用できる場合はprintf
のソースコードを確認する必要があります。
たとえば、私のMacbookでは、プログラムで出力1606416304
を取得します。
そうは言っても、float
を可変個引数関数に渡すと、float
はdouble
として渡されます。したがって、プログラムはa
をdouble
として宣言したのと同じです。
double
のバイトを調べるには、SOに関する最近の質問に対する この回答 を確認できます。
そうしよう:
#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
したがって、double
の最初の4バイトは0であることが判明しました。これが、printf
呼び出しの出力として0
を取得した理由である可能性があります。
より興味深い結果を得るために、プログラムを少し変更することができます。
#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 ...)
float
を実際にはIEEE754倍精度値として表すCコンパイラでは、バイトは(間違いがなければ)
01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000
エンディアンがほとんどない(つまり、最下位バイトが最初に来る)Intel(x86)システムでは、このバイトシーケンスが逆になり、最初の4バイトがゼロになります。つまり、printf
が出力するもの...
IEEE754に準拠した浮動小数点表現については、 このウィキペディアの記事 を参照してください。
これは、floatをバイナリで表現しているためです。整数に変換すると、0のままになります。
未定義の振る舞いを呼び出したため:パラメータータイプについて嘘をついてprintf()メソッドのコントラクトに違反したため、コンパイラーは自由に自由に実行できます。プログラムに「dksjalkはニニーヘッドです!!!」と出力される可能性があります。技術的にはそれでも正しいでしょう。
その理由は、printf()
はかなりばかげた関数だからです。タイプは一切チェックしません。最初の引数がint
であると言う場合(これは%d
で言っていることです)、それはあなたを信じており、int
に必要なバイトだけを取ります。この場合、マシンが4バイトのint
と8バイトのdouble
を使用すると仮定します(float
はprintf()
)、a
の最初の4バイトはゼロになり、これが出力されます。
Floatを整数に自動的に変換しません。どちらもストレージの形式が異なるためです。したがって、変換する場合は、(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など)を使用する必要があります。
整数ではありません。 %f
を使用してみてください。
%dではなく%fが必要
ねえ、それは0を印刷するように何かを印刷しなければなりませんでした。Cでは0が他のすべてであることを忘れないでください!