【问题标题】:Why printf() isn't outputting this integer as float number? [duplicate]为什么 printf() 不将此整数输出为浮点数? [复制]
【发布时间】:2015-02-17 01:23:22
【问题描述】:

我有这个代码:

#include <stdio.h>

int main()
{
    int i = 12345;
    printf("%f", i);

    return 0;
}

printf() 将输出 0.000000,printf() 不应该将 i 中包含的位解释为浮点数吗?

【问题讨论】:

  • 那么 printf 不应该要求 %f, %i,不是吗?
  • 在非技术语言中,你说的是give me an egg,而你期望得到chicken soup。 :-) %f 是浮点数,%i 是整数。提供的变量没有自动类型检查。
  • 启用编译器中的所有警告。如果使用gcc -Wall -Wextra -g,您会收到最近的GCC 警告
  • 如果您希望将整数解释为浮点数,则需要对其进行转换(或者更确切地说,转换指针);在这种情况下,所有赌注都被取消。

标签: c windows printf stdio


【解决方案1】:

这在技术上是未定义的行为,所以任何事情都可能发生,例如@Art 在他的回答中所描述的。

因此,这里的答案是对假设 printf 确实看到正确的 32 位值时尝试将 12345 打印为浮点数会发生什么的解释。

让我们分析您要表示的数字的二进制表示。

使用http://www.h-schmidt.net/FloatConverter/IEEE754.html我们可以看到十进制数12345具有以下32位表示:

decimal      12345
hexadecimal  0x00003039

逐位转换为 IEEE-754 浮点 32 位值,这表示:

float        1.7299E-41

让我们尝试打印它:

#include <stdio.h>

int main() {
    printf("%f\n", 1.7299E-41);
    return 0;
}

打印出来:

0.000000

现在让我们阅读 printf 手册页,如下所示:http://linux.die.net/man/3/printf

f,f

double 参数被四舍五入并转换为样式 [-]ddd.ddd 的十进制表示法,其中小数点字符后的位数等于精度规范。如果精度缺失,则取6;如果精度明确为零,则不会出现小数点字符。如果出现小数点,则在其前面至少出现一位数字。

1.7299E-41的值不能用默认精度的这个标志来表示,所以你得到的结果确实是正确的值。

【讨论】:

  • 很好的论据,但是用不同的 int 试试这个,你仍然会得到 0。我很确定这不是真正的原因。
  • 看来我的演示确实是错误的,我认为调用 UB 是正确的答案,虽然要短得多。
  • 我很确定我的答案是正确的(如果我对 x86_64 的假设成立的话)。但是,是的,UB 是唯一严格正确的答案。
【解决方案2】:

从技术上讲,这是未定义的行为

您应该使用联合来执行此操作并使用%e 而不是%f 打印它,因为它是一个小数字:

#include <stdio.h>
union int2float
{
  int a;
  float b;
};

int main()
{
    union int2float tmp;
    tmp.a = 12345;

    printf("%e\n", tmp.b);

    return 0;
}

这导致1.729903e-41

使用%.100f,您将获得0.0000000000000000000000000000000000000000172990295420898667405534417057140146406548336724655872023410 作为输出。

【讨论】:

  • 请注意,C99 引入了 %a (%A) 格式说明符,允许将数字打印为十六进制浮点数(此处为 0x1.81c8p-136)。它的一个优点是它打印数字就像它以二进制格式存储一样(假设常见的 IEEE-754 格式具有基数为 2 的浮点数,即尾数和指数),而不是十进制近似值。
【解决方案3】:

printf 没有将i 中的位解释为浮点数的最可能原因是printf 根本看不到i。我怀疑您正在运行 x86_64 或类似的平台,其中参数被传递给寄存器中的函数。通常printf 会将您的 int 解释为您提供的任何格式说明符。但是函数的浮点参数在 x86_64 上的处理方式不同,并且被放入不同的寄存器中。所以你得到 0 作为输出的原因是printf 在格式字符串之后提取第一个浮点参数而不是第一个通用寄存器并打印出来。由于您在调用 printf 之前没有使用任何浮点寄存器,因此它们很可能从程序开始就被归零。

您可以尝试以下实验:

#include <stdio.h>

int
main(int argc, char **argv)
{
        printf("%f\n", 17.42);
        printf("%f\n", 0x87654321);
        return 0;
}

我得到这个输出:

$ ./foo
17.420000
17.420000

说了这么多,唯一正确的答案是:这是未定义的行为,不要这样做。但是看看幕后发生的事情很有趣。

如果您想深入研究并在 Linux/MacOS/*BSD 上运行,this document, section 3.2.3 描述了如何将参数传递给函数(包括像 printf 这样的可变参数函数)。 Windows 做的事情略有不同,但在这种情况下,Windows 上的结果将完全相同。 Linux/MacOS/*BSD 上的printf 期望参数以%xmm0(Windows 上的%xmm1)打印,而您的调用将其传递给%rsi(Windows 上的%rdx)。

当然,如果我对 x86_64 有误,请忽略我所说的一切并查看@SirDarius 的答案,因为这适用于许多(尤其是较旧的)架构。如果您使用的是 alpha、sparc、powerpc、arm 等,那么您只能靠自己了。使用 int 调用 printf 一次,使用 float 调用一次,反汇编编译的代码并查看参数是如何传递的。

【讨论】:

  • 我必须为此 +1。但是由于某种原因,我得到 17.420000,然后是 17.420006,其代码完全相同(gcc(rev5,由 MinGW-W64 项目构建)4.8.1)64 位。
  • @SirDarius 可能是因为 printf 实现以不保留浮点寄存器的方式接触浮点寄存器。我的示例代码不仅为您提供了一种,而且还为您提供了两种未定义的行为。
  • 啊,我的错,GCC 是 32 位的。 64 位版本输出 17.420000 和 0.000000。 UBfest 是 :)
【解决方案4】:

如果要将 int 打印为浮点数,请将其强制转换:

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

【讨论】:

  • 我想他宁愿期待printf("%f", *((float*)&amp;i)) 的结果。至少那是我从“将 i 中包含的位解释为浮点数”中得到的。而这个答案并不能解释为什么这不是他得到的。
【解决方案5】:

printf() 知道格式化字符串之后的参数的数据类型(及其计数)的唯一方法是读取格式字符串。整数在内存中的表示不同于浮点数。您以整数格式传递 printf() 数据,但它采用浮点数格式。这就是你在控制台中得到垃圾的原因。您可以通过类型转换来纠正此问题。

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

【讨论】:

  • 除了他没有得到垃圾,他得到了 0。他期望垃圾(或者更确切地说 int 的位被解释为浮点数),他在问为什么这不是他得到的。
  • @sepp2k 你说得对,我误读了那部分的问题。我在尝试 OPs sn-p 时得到了。
【解决方案6】:

当您使用%f 格式说明符时,您必须使用浮点变量并打印值。 但是您已将i 声明为整数并尝试使用%f 进行打印,它将打印一些值。 因为它是一个整数,所以您必须使用 %d 说明符。 在printf %f 中将搜索浮点变量。但是不会有浮点变量,所以它会打印一些值。

您可以在打印时键入转换整数值。 printf("%f",(float)i);

【讨论】:

    【解决方案7】:

    要将i 设为float 类型,您需要进行类型转换。

    你可以使用

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

    示例。

    float myFloat;
    int myInt; 
    myFloat = (float)myInt ;        // Type casting
    

    【讨论】:

      【解决方案8】:

      试试这个

      int i = 12345;
      printf("%.2f", i/1.0f);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-03-21
        • 1970-01-01
        • 2014-02-25
        • 2022-01-06
        • 1970-01-01
        • 1970-01-01
        • 2022-01-14
        • 1970-01-01
        相关资源
        最近更新 更多