【问题标题】:Why does printf print an integer as a double? [duplicate]为什么 printf 将整数打印为双精度数? [复制]
【发布时间】:2020-03-21 10:43:33
【问题描述】:
printf("%f", 1.0); //prints 1.0

但是

printf("%f", 1);  // prints 0.0

转换是如何发生的?

【问题讨论】:

    标签: c printf


    【解决方案1】:

    根据下面的@Eric Postpischil's 评论不同。

    The first double argument (or float argument, which will be promoted to double if used with the ... part of a function) is put in %xmm0. The first “normal integer class” type of argument would go into %rdi. For printf, though, that pointer to the format string is the first argument of that class, so it does into %rdi. That means the first int argument passed goes into the second register for that class, which is %rsi. So, with printf("%f", 1);, printf looks for the floating-point value in %xmm0, but the caller puts an int 1 in %rsi

    【讨论】:

    • 我理解你,但我坚持简单,没有这么难的事情,
    • 断言代表int 的字节将被printf 解释为浮点数是不正确的。在一些常见的 C 实现中,尤其是使用 System V x86-64 ABI 的任何实现中,第一个 double 参数在 %xmm0 寄存器中传递,而 int 参数在传递格式字符串后传递给 printf %rsi。在处理%f 时,printf 会看到%xmm0 中的字节,而不是%rsi. 中的字节
    • @EricPostpischil,非常感谢您让我知道这一点。你的意思是说,因为“1”是一个整数常量,它将被放置在非 %xmm 寄存器中,当 printf 扫描这个 %f 时,它会检查 %xmm0 并且它将获得寄存器具有的任何值。如果我错了,请纠正我。
    • @MuraliKrishnaBellamkonda:是的。第一个double 参数(或float 参数,如果与函数的... 部分一起使用,将提升为double)放入%xmm0。第一个“普通整数类”类型的参数将进入%rdi。但是,对于printf,指向格式字符串的指针是该类的第一个参数,因此它确实存在于%rdi 中。这意味着传递的第一个int 参数进入该类的第二个寄存器,即%rsi. 因此,对于printf("%f", 1);printf%xmm0 中查找浮点值,但调用者放置了一个int%rsi 中的 1 个。
    • 现在知道了。再次感谢。我有一个疑问。它在 32 位机器中的行为方式。
    【解决方案2】:

    并不是每个编译器都这样,有些实际上是打印 1.0。但是当指示 printf 打印一个双精度值时,您必须传递一个双精度值,而不是一个整数。你总是可以使用类型转换:

    printf("%f", (double)1);
    

    【讨论】:

    • gcc 和 visual studio 打印 0.0 ,也不能练习使用
    • 我不认为这是 compiler 特定的。它(几乎但可以想象)可能是特定于 CPU 的。
    • 你能识别出任何在给定printf("%f", 1);的情况下打印“1.0”的编译器吗?
    • 在 macOS catalina 上的 clang 产生 1.000000
    【解决方案3】:

    printf("%f", 1); 导致未定义的行为,因为 double 是预期的,但您传递了 int。没有解释为什么它打印0.0,因为行为是未定义的。

    【讨论】:

    • 有一个解释为什么它打印“0.0”。 C标准没有给出解释,但是编译器的源代码、系统的ABI、硬件架构文档等等都有解释。
    • @EricPostpischil 因为没有指定实现,所以问题仅与 C 编程语言有关。该语言不提供任何推理,因此提供了答案。
    • C 标准将“未定义”定义为“行为……或本文档没有要求的行为”(强调指出相关概念)。这意味着“C 标准中没有解释”的说法是正确的。从字面上看,“没有解释”的说法是错误的。有人可能会断言限定词要从上下文中理解。不幸的是,这也是错误的:人们无法从上下文中理解这一点。 Stack Overflow 充斥着错误的概念,即 C 标准是理解 C 程序行为的全部和全部。
    • 任何称职的软件工程师必须了解编译器的工作原理、程序的优化功能、程序在堆栈和数据段中的组织方式以及地址引用的工作原理,什么样的事情会在程序中出错,什么可观察到的症状是什么错误的线索,等等。只有通过学习比 C 标准中的内容更多的解释才能学习这些东西。告诉人们没有对 C 标准未定义的行为的解释是有害的。
    • @EricPostpischil 虽然您提到的事情很重要,但试图解释未定义的行为本质上是错误的。 C 是一种高级语言——它是一种抽象。告诉人们不要以这种抽象方式思考是有害的。
    【解决方案4】:

    问题不在于 printf 函数本身,而在于编译器是否足够聪明。如果您的编译器不够聪明,那么它会将 printf 视为普通函数调用,并且不知道该函数的参数语法。所以它只是将一个字符串和一个整数放在堆栈上并调用该函数。 printf 函数接受第一个参数并开始将其解析为格式字符串。当它看到格式说明符%f 时,它会尝试将堆栈中的相应内存部分解释为浮点数。无法知道编译器之前将 int 值推到了那里。所以 printf 最好将内存解释为浮点数。结果取决于平台,即取决于 endiness 和 float/int 大小,还包括随机性,因为您很可能会在堆栈上遇到一些垃圾。在这种情况下 printf 所做的转换也可以这样看:

    int i = 1;               // Integer variable
    int* pi = &i;            // Pointer to i 
    float* pf = (float*)pi;  // Reinterpret the pointer as floating point number address
    float f = *pf;           // Get the floating point from this address
    printf("%f\n", f);
    

    【讨论】:

    • 在一些常见的 C 实现中,例如使用 System V x86-64 ABI 的任何东西,字符串和 int 都不会放入堆栈。指向字符串第一个字符的指针被放入寄存器,int 被放入另一个寄存器。如果传递了double 而不是int 1,它将被放入一个完全不同的寄存器中。结果是,当printf%f 查找double 时,它看不到int 1 的字节。它看到不同寄存器中的字节。
    • 我同意很高兴了解幕后发生的事情,但由于它确实是特定于平台的,您应该说出您的解释涵盖哪些平台。
    【解决方案5】:

    printf() 这里的东西除了接收基于你传入的格式的浮点数,在printf() 中将 int 打印为浮点数,你必须强制转换它

    printf("%f", (float)1);
    

    printf("%f",(double)1);
    

    因为 C 会根据变量的类型和内存表示来处理传递给 printf() 的变量,并且您传递了错误的值,这将导致未定义的行为。

    【讨论】:

      猜你喜欢
      • 2020-10-13
      • 2015-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-12
      • 2019-05-22
      • 2019-02-09
      相关资源
      最近更新 更多