web-dev-qa-db-ja.com

このプログラムはどのように機能しますか?

#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", a);
    return 0;
}

0を表示します!!そんなことがあるものか?理由は何ですか?


printfの動作を調査するためにprintfステートメントに%dを意図的に配置しました。

88
Lazer

これは、_%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*, ...)であるため、floatdoubleに変換される理由については、6.5.2.2/7から

関数プロトタイプ宣言子の省略記号により、最後に宣言されたパラメーターの後で引数型の変換が停止します。 デフォルトの引数の昇格は末尾の引数で実行されます。

および6.5.2.2/6から、

呼び出された関数を表す式の型がプロトタイプを含まない場合、整数の昇格は各引数で実行されますおよび型floatの引数はdoubleに昇格されます。これらは(デフォルトの引数プロモーション

(これを見つけてくれてありがとうAlok。)

238
kennytm

技術的に言えば、theprintfはなく、各ライブラリは独自のライブラリを実装しているため、printfあなたがしていることをすることによる行動は、あまり役に立たないでしょう。システムでのprintfの動作を調べようとしている可能性があります。その場合は、ドキュメントを読み、ライブラリで使用できる場合はprintfのソースコードを確認する必要があります。

たとえば、私のMacbookでは、プログラムで出力1606416304を取得します。

そうは言っても、floatを可変個引数関数に渡すと、floatdoubleとして渡されます。したがって、プログラムはadoubleとして宣言したのと同じです。

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
45
Alok Singhal

%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に準拠した浮動小数点表現については、 このウィキペディアの記事 を参照してください。

20
MartinStettner

これは、floatをバイナリで表現しているためです。整数に変換すると、0のままになります。

7
Shaihi

未定義の振る舞いを呼び出したため:パラメータータイプについて嘘をついてprintf()メソッドのコントラクトに違反したため、コンパイラーは自由に自由に実行できます。プログラムに「dksjalkはニニーヘッドです!!!」と出力される可能性があります。技術的にはそれでも正しいでしょう。

7
Kilian Foth

その理由は、printf()はかなりばかげた関数だからです。タイプは一切チェックしません。最初の引数がintであると言う場合(これは%dで言っていることです)、それはあなたを信じており、intに必要なバイトだけを取ります。この場合、マシンが4バイトのintと8バイトのdoubleを使用すると仮定します(floatprintf())、aの最初の4バイトはゼロになり、これが出力されます。

5
Gorpik

Floatを整数に自動的に変換しません。どちらもストレージの形式が異なるためです。したがって、変換する場合は、(int)型キャストを使用します。

#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", (int)a);
    return 0;
}
3
sganesh

C++でもタグ付けしたので、 このコード はおそらく期待どおりに変換を行います。

#include <iostream.h>

int main() {
    float a = 1234.5f;
    std::cout << a << " " << (int)a << "\n";
    return 0;
}

出力:

1234.5 1234
2
Mizipzor

%dは10進数です

%fはフロートです

これらの詳細を参照してください ここ

浮動小数点数と整数の表現が異なるため、0になります。

1
Filip Ekberg

関連するデータ型(int、float、stringなど)で適切なフォーマット指定子(%d、%f、%sなど)を使用する必要があります。

整数ではありません。 %fを使用してみてください。

0
tangrs

%dではなく%fが必要

0
S.C. Madsen

ねえ、それは0を印刷するように何かを印刷しなければなりませんでした。Cでは0が他のすべてであることを忘れないでください!

0
chunkyguy