【问题标题】:C programming printf("%d", 50.2);C 编程 printf("%d", 50.2);
【发布时间】:2017-05-17 12:22:24
【问题描述】:
#include <stdio.h>

int main(void) {
    printf("%d\n", 50.2);
    return 0;
}

~ 当我执行它时。

50.2 的二进制是 (0100 0010 0100 1000 1100 1100 1100 1101)。

所以,我预计是 1,112,067,277,但是,这些值不是,也不是固定的。

值每次都改变,为什么?

【问题讨论】:

  • 检查你的代码
  • 您需要使用gcc -Wall -g yoursource.c -o yourprog 进行编译(所有警告和调试信息),改进代码以获得没有警告,然后运行./yourprog 并使用gdb ./yourprog 进行调试
  • 请不要张贴纯文本输出的截图。只需将文本复制并粘贴到您的问题中即可。
  • don't put screenshots of text output, code or error。改为复制并粘贴到此处
  • 您可能想查找“未定义行为”中的“未定义”一词的含义。

标签: c floating-point int printf


【解决方案1】:

问题是您在 printf 中使用了错误的格式说明符 %d 而不是 %f。这会调用未定义的行为。

每当您调用未定义的行为时,都不会有任何确定性或预期的输出。当您有未定义的行为时,编译器可以继续并做出奇怪的假设,例如“这个浮点文字从未使用过,因此无需为其分配内存”。这意味着您最终可能会打印垃圾内存位置,甚至导致程序崩溃。因此,分析为什么未定义的行为会产生特定结果并不是一项有意义的任务。

为了保证确定性行为,您必须执行以下操作:

#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

这是在一台机器上,其中 double 为 8 个字节,整数为 4 个字节/小端,这意味着您将获得 4 个最低有效字节作为一些无意义的输出(即数字 9999999Ah)。

【讨论】:

  • 不是写给工会的一个成员,然后从另一个 UB 读取吗?
  • @RudyVelthuis 在 C++ 中它始终是 UB,但在 C 中不一定是 UB。C 根据 C11 6.5.2.3/3 允许它。见脚注 95:“如果用于读取联合对象内容的成员与上次用于在对象中存储值的成员不同,则将值的对象表示的适当部分重新解释为对象6.2.6 中描述的新类型的表示(有时称为“类型双关”的过程)。这可能是一个陷阱表示。”。这意味着它可能是也可能不是 C 中的 UB,这取决于系统是否支持陷阱表示。
  • 然而,一个字符类型(其中我们可以安全地算上uint8_t,因为编译器是健全的)永远不会有陷阱表示。因此,将 C 中的任何数据类型拆分为字节总是安全的,但不一定反过来。
  • unsigned char 不是“only 永远不能有陷阱表示的类型”。可选类型(u)int(8,16,32,64)_t 也没有。
  • 指针别名与此无关,但相关部分将是指针转换的规则 6.3.2.3,其中他们提到了对齐问题:“指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未正确对齐引用类型,则行为未定义。"
【解决方案2】:

这是因为,对格式说明符使用错误的参数类型会调用 undefined behavior

引用C11,第 7.21.6.1 章,fprintf()

d,i      int 参数转换为有符号十进制

这表明%d 需要int 类型的参数,并且,

[....] 如果有任何参数 不是相应转换规范的正确类型,行为是 未定义。

所以,您传递的是 double一般来说,即使是 float 参数也会被提升为 double)其中应该是 int

【讨论】:

  • @StoryTeller 没错,它是一个double 字面量,只是想暗示如果还有一个浮点变量,它就会被提升。
【解决方案3】:

看似随机值的原因是,在您的平台上printf() 期望在浮点/双精度参数的其他寄存器中使用整数参数。例如在 x86_64 上 %esi (int) vs. %xmm0 (float)

根据 C 标准,这是可以的,因为由于错误的转换说明符,您会调用未定义的行为。

【讨论】:

  • FWIW,您的“%esi vs. %xmm0”适用于 Linux x86_64。在 Windows x86_64 中,它是 RCX 与 XMM0。同样的问题,不同的寄存器。
猜你喜欢
  • 1970-01-01
  • 2011-01-17
  • 2023-03-04
  • 1970-01-01
  • 1970-01-01
  • 2015-12-09
  • 1970-01-01
  • 2014-09-26
  • 1970-01-01
相关资源
最近更新 更多