【问题标题】:Why does this output of the same expression from printf differ from cout?为什么 printf 的相同表达式的输出与 cout 不同?
【发布时间】:2013-09-30 20:03:10
【问题描述】:

我正在使用 Visual C++ 2012 并从命令行编译以下文件:

#include <stdio.h>
int main()
{
    printf("%.5f", 18/4+18%4);
    return 0;
} 

链接 MSVCRT.LIB 而不是 LIBCMT 以避免运行时错误 R6002。
该程序的输出值为 0.00000。

但是,如果我在 C++ 中执行完全相同的操作

 #include <iostream>
 using namespace std;
 int main()
 {
      cout << 18/4+18%4 << endl;
      return 0;
 }

现在,它打印出 6,就像它应该的那样。

有什么区别?是与语言本身(C 与 C++)还是输出方法(cout 与 printf)有关,还是只是 MSVC 的一个怪癖?

【问题讨论】:

  • 尝试使用"%d" 而不是"%.5f"
  • 好像你的第一个代码示例 printf 格式需要一个浮点数,但算术是整数算术。
  • 顺便说一句,他们没有从 C++ 中删除 printf,所以您可以在那里尝试并立即发现问题所在。
  • 一个很好的演示为什么强类型 cout 比 loosy goosy printf 更好
  • @congusbongus - printf 代码编译并做了错误的事情,而不是开发人员的意图,实际上调用了未定义的行为。 cout 完全符合开发人员的预期

标签: c++ c floating-point printf cout


【解决方案1】:

表达式18/4+18%4 的计算结果为int,并且您正在请求浮点数。您应该始终在启用警告的情况下进行编译,并注意它们(他们说警告是等待发生的错误,他们是对的)。

这是我的编译器 (GCC 4.8.1) 告诉我的(即使没有强制执行 -Wall):

warning: format ‘%.5f’ expects type ‘double’, but argument 2 has type ‘int’

另一方面,std::cout&lt;&lt; 操作能够推断出您的表情类型并将其正确地流式传输到您的屏幕。

【讨论】:

  • 18/4+18%4 的类型是int,而不是“整数”。
  • %.5f 格式需要double,而不是float。 (printffloat 参数被提升为 double 但在这种情况下不相关。)
  • 微小的修正——不要注意警告。使用-Werror 这样他们就会停止你的编译。
【解决方案2】:

C 函数正在传递一个整数,但您告诉它(使用%f)期待一个双精度浮点数,所以它失败了。 C++ 函数知道它正在传递一个整数,因此它可以正常工作。

【讨论】:

  • printf 没有失败(go kaboom)。它只是没有按预期工作。
  • @DavidHammen:它确实无法正确工作,但仅仅因为它为他打印 0 并不意味着它会为每个人打印 0。对于某些人来说,它可能会大放异彩。
  • C11dr 7.21.6.1 9 "如果任何参数不是相应转换规范的正确类型,则行为未定义。"
  • @chux:确实,“kaboom”实际上是允许的结果之一! :)
【解决方案3】:

C 示例中,此表达式 18/4+18%4 将计算为 int,因为所有操作数都是 整数常量,但您指定的是它是printfdouble,因此将被错误地处理。另一方面,如果您在表达式的除法部分使用了浮动常量,例如18.0/4+18%4,则整个表达式的计算结果将是一个double。或者,您也可以在格式说明符中使用 "%d"

这也是undefined behavior 错误地将格式指定为printf,这也说明了为什么使用警告构建很重要,使用gcc -Wall 我收到以下警告(看到它live):

warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ 

C++中,std::coutoperator<<int 有一个重载,因此在这种情况下会调用它。我们可以看到 C++ draft standard 需要许多其他重载,在部分 27.7.3.1 类模板 basic_ostream 我们找到以下运算符声明:

basic_ostream<charT,traits>& operator<<(int n);

为了完整起见,回到未定义的行为,7.19.6.1 部分中的C99 draft standard fprintf 函数 printf 的部分引用了格式字符串段落 9 说:

如果转换规范无效,则行为未定义。[...]

【讨论】:

  • 显然gccclang 会对此发出警告,而不管警告设置如何,这很好,但这并不能改变启用警告将防止许多错误的事实。
【解决方案4】:

表达式

18 / 4 + 18 % 4

计算为 int

printf 格式字符串 "%.5f" 需要双精度。

使用 c++ 和 ostreams,语言可以自动确定输出类型。

只需将您的 C 代码更改为以下内容:

#include <stdio.h>
int main()
{
    printf("%d", 18 / 4 + 18 % 4);
    return 0;
}

【讨论】:

    【解决方案5】:

    其他人已正确指出您的 printf() 语句中的 int/double 不匹配。我只想评论您的声明,“与 MSVCRT.LIB 而不是 LIBCMT 链接以避免运行时错误 R6002。”如此简单的程序不应该对运行时环境造成过度负担,因此像这样的运行时错误应该是代码中未定义行为的危险信号。

    “MSVCRT R6002”的快速谷歌说:

    C 运行时错误 R6002 浮点支持未加载

    未链接必要的浮点库。 通过检查以下可能的原因来修复

    1. 该程序是使用需要协处理器的选项(例如 /FPi87)编译或链接的,但该程序是在未安装协处理器的计算机上运行的。
    2. printf_s 或 scanf_s 函数的格式字符串包含浮点格式规范,并且程序不包含任何浮点值或变量。
    3. 编译器通过仅在必要时加载浮点支持来最小化程序的大小。编译器无法检测格式字符串中的浮点格式规范,因此它不会加载必要的浮点例程。
    4. 使用浮点参数来对应浮点格式规范,或者在程序的其他地方执行浮点赋值。这会导致加载浮点支持。
    5. 在混合语言程序中,链接程序时,在 FORTRAN 库之前指定了 C 库。最后重新链接并指定 C 库。

    当然,这里的教训是您应该密切注意编译器和运行时警告和错误。如有疑问,请始终假设问题出在您的代码中,而不是在编译器中。

    【讨论】:

    • 我只是想链接到一个内置浮点支持的库(MSVCRT 支持)然后它会解决问题。不过,我现在明白这个错误了。
    【解决方案6】:

    在 C 中,因为您在 printf 格式说明符中明确指定了浮点(“%f”),所以它需要一个浮点参数。但是你给它一个“int”参数,所以问题就来了。

    根据您要执行的操作,您可以:

    1) 将您的(否则为 整数)表达式转换为浮点数

    和/或

    2) 在 cout 流中使用 setprecision,就像在 C 中使用 "%.5f" 一样:

    #include <iostream>
    using namespace std;
    int main()
    {
       float x = 18/4+18%4;
       std::cout << std::setprecision(5) << x << endl;
       return 0;
    }
    

    3) 如果您想要整数,请使用printf ("%d", 18/4+18%4);

    【讨论】:

      【解决方案7】:

      如果您想要相同的行为(和响应),您最好编写代码

      printf("%d\n", 18/4 + 18%4);
      

      得到一个 int 响应而不是一个浮点响应。你会得到与&lt;&lt; operator selected is 相同的结果

      ostream&amp; std::operator&lt;&lt;(ostream&amp;, const int);

      否则,你可以显式使用

      printf("%.5f\n", (double)(18/4 + 18%4));
      

      得到6.00000的结果。

      【讨论】:

        【解决方案8】:

        让你的数字之一是浮点值:

        #include <stdio.h>
        int main()
        {
        
            printf("%.0f", 18/4.0+18%4);
            return 0;
        } 
        

        【讨论】:

          【解决方案9】:

          一种可能的替代方法是对文字(或表达式本身)进行类型转换:

          #include <stdio.h>
          
          int main(void)
          {
              printf("%.5f", (float)18/4+18%4);
              return 0;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-06-12
            • 2017-05-21
            • 1970-01-01
            相关资源
            最近更新 更多