4
printf("line 5: %f\n",98);  //output is 0.000000
printf("line 6: %f\n",98.98); //output is 98.980000  
printf("line 5: %f\n",98);//though same as first printf statement but output is 98.979980

虽然第一个和最后一个 printf 语句完全相同,但它们的输出不同。为什么?

因为当它期待浮点数时,一个 int 被传递给 printf,这就是它工作奇怪的原因。但我的观点是,为什么在最后一个打印语句中,不是打印一些垃圾值或 0,而是使用第二个 printf 语句的值,这就是打印的内容。

4

5 回答 5

5

正如其他人已经说过的那样,当它期望 a 时传递一个intto 会导致未定义的行为,并且任何事情都可能发生。您可能对程序打印在第三行而不是随机数的原因感兴趣。printfdouble98.979980

参数被传递到printf堆栈上。当第 2 行通过98.98时,printf它被压入堆栈,数字的最低有效部分在前。

然后printf返回,并在第三行再次调用它,现在将其98压入堆栈。在您的架构上,int类型似乎是 32 位;类型大小的一半,所以这只会覆盖之前在堆栈中double的下半部分。98.9898.98 的上半部分仍在堆栈中。

现在第三次调用从堆栈中printf读取 a 。double它读取的最重要的一半来自98.98较早在堆栈上的 ,而不太重要的一半来自 ; 的二进制表示98。这就是结果如此接近的原因98.98。由于 98 是一个如此小的数字,它的最高有效位将为 0,并且将最低有效的一半设置98.98为大部分为零会给你一个更小的数字。

如果第 3 行使用了更多位设置为 1 的数字,您将得到大于98.98. 例如,-1 的二进制表示将其所有位设置为 1,您会得到:

printf("line 2: %f\n", 98.98); # 98.98
printf("line 3: %f\n", -1);    # 98.980042

如果编译器使用 64 位整数,或者double先传递最高有效部分的 s,或者使用寄存器而不是堆栈来传递参数,你会得到非常不同的结果。

于 2013-07-07T17:49:43.480 回答
4

因为您的程序调用了未定义的行为。 98是类型int,但%f需要一个float(或double,由于默认的促销规则)。

因此,由于printf()在转换说明符的类型和实际类型不匹配时有 UB,所以对于它所做的任何事情都没有合理的解释。

于 2013-07-07T17:26:13.583 回答
2

这是因为%f需要双参数。给出一个int未定义的行为。

ISO/IEC 9899:1999 , §7.19.6.1, 9 :

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

未定义行为是指行为不可预测的计算机代码。

至少使用 gcc 如果启用了警告,您将收到相应的警告:

警告:格式“%f”需要类型“double”,但参数 2 的类型为“int”

于 2013-07-07T17:26:35.443 回答
2

%f期望double,但您正在传递一个int值。这是未定义的行为。

正确的应该是:

printf("line 5: %f\n",98.0); 
printf("line 6: %f\n",98.98); 
printf("line 5: %f\n",98.0);
于 2013-07-07T17:27:11.983 回答
1

如果我们查看编译器生成的代码,我们会看到以下内容:

00401B5E|>MOV DWORD PTR SS:[ESP+0x4],0x62                          ; |||
00401B66|>MOV DWORD PTR SS:[ESP],arma_sto.00404024                 ; |||ASCII "line 5: %f\n"
00401B6D|>CALL <JMP.&msvcrt.printf>                                ; ||\printf
00401B72|>MOV DWORD PTR SS:[ESP+0x4],0x51EB851F                    ; ||
00401B7A|>MOV DWORD PTR SS:[ESP+0x8],0x4058BEB8                    ; ||
00401B82|>MOV DWORD PTR SS:[ESP],arma_sto.00404030                 ; ||ASCII "line 6: %f\n"
00401B89|>CALL <JMP.&msvcrt.printf>                                ; |\printf
00401B8E|>MOV DWORD PTR SS:[ESP+0x4],0x62                          ; |
00401B96|>MOV DWORD PTR SS:[ESP],arma_sto.00404024                 ; |ASCII "line 5: %f\n"
00401B9D|>CALL <JMP.&msvcrt.printf>                                ; \printf

因为您没有将这两个 98 值转换为浮点数,所以输出是随机的(基于堆栈)。%f 的有效输入是一个浮点数,它在堆栈上占用两个条目。

第 4 行不起作用,因为您只提供了一个堆栈条目。

第 5 行工作正常,因为 98.98 是一个浮点数(需要两个堆栈条目)

第 6 行输出 ~98.98,因为 00401B7A 处的 MOV 没有撤消。这意味着第 6 行输出了一个有效的浮点数,因为它有两个堆栈条目,但由于前一个数字还剩下一部分,所以输出了错误的浮点数。

解决方案,转换为浮点数

printf("line 5: %f\n",(float)98);  //output is 98.000000
printf("line 6: %f\n",98.98); //output is 98.980000  
printf("line 5: %f\n",(float)98); //output is 98.000000
于 2013-07-07T17:32:22.223 回答