【问题标题】:What is the underlying logic behind printf function? [duplicate]printf 函数背后的底层逻辑是什么? [复制]
【发布时间】:2021-03-30 05:41:56
【问题描述】:

最近,我在玩 C 来测试不同的东西。

printf("%d", 0.4);

将打印 -1717986918。

我知道这一定与浮点数的二进制表示有关。但是,使用一些在线资源将 0.4 转换为浮点数后,二进制表示与 -171986918 不匹配。请解释一下这段代码背后到底发生了什么。

PS:我正在使用 gcc 编译器并在 Windows 64 位上运行它。

【问题讨论】:

  • sizeof(int)sizeof(double)0.4 可能是)具有不同的字节数(很可能)。
  • 常量0.4 的类型为double。因此,它与%d 指令没有正确匹配,导致您的printf 调用具有未定义的行为。 “实际发生”的情况取决于您对printf 的实施细节,也可能取决于其他因素。除了学习必须正确键入将printf 参数与格式字符串匹配的课程之外,这不是一个有用的细节。无论您发现什么细节都无法可靠地转移。
  • 请注意,如果您启用警告(例如gcc -Wall),printf 语句将被编译器标记。
  • 一些系统——macOS Mojave 10.14.6 是一个,但我相信 Linux x86/64 是另一个——有一个 ABI 可以在不同的寄存器集中传递整数和双精度值。这意味着如果您没有将编译器设置为 fussy,您会从 printf("Integer: %4d; double: %13.6g\n", 3.141592654, 9876); 获得“正确”结果——也就是说,它会产生“Integer: 9876; double: 3.14159, give or take some spaces. That's because when printf()` 处理 @987654335 @,它从整数寄存器中取出第一个整数,当它处理%13.6g时,它从浮点寄存器中取出第一个双精度数。
  • 你不能依赖这种行为。大多数历史系统不会那样做。但是(至少有一些)正在使用的 x86/64 ABI 会导致这种滥用。

标签: c floating-point printf


【解决方案1】:

我明白这一定与二进制表示有关

不,这是未定义的行为 (UB)。


"%d"double 不匹配。

如果转换规范无效,则行为未定义。 C17dr § 7.21.6.1 9

输出和行为未由语言定义 (UB)。任何事情都可能发生。


在某些系统上,UB 可能会看到二进制模式的一部分被解释为 int

在另一个系统上,intdouble 以不同的方式传递,看到的输出基于垃圾。

在另一个系统上或只是另一天,事情发生了。

都是未定义的行为(UB)。


解释到底发生了什么

要了解 OP 的机器中可能发生的情况,请使用十六进制输出。

printf("%x\n", (unsigned) -1717986918);  // UB to print a negative  `int` with `"%x"`
printf("%a\n", 0.4);

9999999a
0x1.999999999999ap-2

因此,0.4 有效位的最低有效 4 个字节似乎被解释为 int
仍然是 UB。任何事情都可能发生。

【讨论】:

    猜你喜欢
    • 2017-10-17
    • 1970-01-01
    • 2022-08-14
    • 2014-07-15
    • 1970-01-01
    • 2012-11-19
    • 2020-09-08
    • 1970-01-01
    • 2021-01-14
    相关资源
    最近更新 更多