#include <stdio.h>
int main(void) {
printf("%d\n", 50.2);
return 0;
}
〜私はそれを実行します。 50.2のCプログラミングprintf( "%d"、50.2);
バイナリは(0100 1000年1100 1100 1100 1101 0100 0010)です。
私は1,112,067,277を期待しましたが、その値は固定でも固定でもありません。
値は毎回変更されますが、なぜですか?
#include <stdio.h>
int main(void) {
printf("%d\n", 50.2);
return 0;
}
〜私はそれを実行します。 50.2のCプログラミングprintf( "%d"、50.2);
バイナリは(0100 1000年1100 1100 1100 1101 0100 0010)です。
私は1,112,067,277を期待しましたが、その値は固定でも固定でもありません。
値は毎回変更されますが、なぜですか?
問題は、printfに%f
の代わりに%d
の形式指定子を間違って使用していることです。これにより、未定義の動作が呼び出されます。
未定義のビヘイビアを呼び出すと、決定的な出力や予期しない出力はありません。未定義の振る舞いを持つとき、コンパイラは "この浮動小数点定数は決して使用されないので、メモリを割り当てる必要はありません"というような奇妙な仮定をすることができます。つまり、ガベージメモリの場所が表示されたり、プログラムがクラッシュすることさえあります。したがって、未定義の動作が特定の結果をもたらした理由を分析することは意味のある作業ではありません。
確定的な動作を保証するために、あなたはこのような何かをしなければならないでしょう:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
int main(void)
{
const double number = 50.2;
union
{
double d;
uint8_t u8 [sizeof(double)];
} u = { number };
printf("Binary representation of %f:\n", number);
for(size_t i=0; i<sizeof(double); i++)
{
printf("%.2" PRIx8 " ", u.u8[i]);
}
printf("\n\n");
printf("Integer (nonsense) representation of %f:\n", number);
int i;
memcpy(&i, u.u8, sizeof(int));
printf("%d", i);
return 0;
}
出力:
Binary representation of 50.200000:
9a 99 99 99 99 19 49 40
Integer (nonsense) representation of 50.200000:
-1717986918
これは二重のは、8バイトの整数少し4バイト/だったマシン上にありましたこれは意味のない出力として4つの最下位バイトを取得することを意味します(つまり番号9999999Ah)。
ユニオンの一員に書き込みをしておらず、別のUBからも読んでいませんか? –
@RudyVelthuis C++では常にUBですが、必ずしもC言語ではありません。Cでは、C11 6.5.2.3/3に準拠しています。フットノート95を参照してください: "ユニオンオブジェクトの内容を読み取るために使用されたメンバが最後に使用されたメンバと同じでない場合、オブジェクトの値をオブジェクトに格納するために、 が値のオブジェクト表現の適切な部分を再解釈します6.2.6で記述されているような新しい型のオブジェクト表現(プロセスは「型打ち」と呼ばれることもあります)がトラップ表現である可能性があります。システムがトラップ表現をサポートしているかどうかに応じて、CのUBであるかどうかを意味します。 – Lundin
しかし、文字型(その中でコンパイラが正当であると仮定すると、 'uint8_t'を安全に数えることができます)は決してトラップ表現を持つことはできません。したがって、Cのデータ型をバイトに分割することは常に安全ですが、必ずしもそうではありません。 – Lundin
これは、フォーマット指定子に間違った引数型を使用すると、undefined behaviorが呼び出されるからです。 C11
、章§7.21.6.1を引用
、fprintf()
d
、i
int
引数は%d
タイプの引数を期待することが指示符号付き10進
に変換されます。 int
および
[....]引数が で、対応する変換仕様のタイプが正しくない場合、動作は 未定義です。
だから、あなたはdouble
渡しているint
が期待されている(一般的にはを、さえfloat
引数もdouble
。に昇格ます)。
@StoryTeller右、それは 'double'リテラルです。ちょうどフロート変数がある場合には、それが宣伝されます。 –
一見無作為な値の理由は、プラットフォーム上のprintf()
は、浮動小数点数/倍精度浮動小数点数型の他のレジスタでは整数引数が必要です。あなたがあるため、誤った変換指定子の未定義の振る舞いを呼び出すので、例えばx86_64版では%は、ESI(int型)対C標準によると% XMM0(フロート)
これは、okです。
FWIW、 "%esiと%xmm0"はLinux x86_64用です。 Windows x86_64ではRCX vs. XMM0です。同じ問題、異なるレジスタ。 –
あなたのコードをチェックしてください –
あなたは 'gcc -Wall -g yoursource.c -o yourprog'(すべての警告とデバッグ情報)でコンパイルする必要があります。警告を表示しないようにコードを改良し、'。/ yourprog'を実行してデバッグします'gdb。/ yourprog'と一緒に –
プレーンテキスト出力用のスクリーンショットを投稿しないでください。あなたの質問にテキストをコピー&ペーストするだけです。 – Gerhardh