9

pow()在 C 编程语言中为函数使用整数转换时遇到了一些问题。我使用的编译器是适用于 Windows 平台的Tiny C Compiler(tcc 版本 0.9.24)。执行以下代码时,它会输出意外结果100, 99

#include <stdio.h>
#include <math.h>

int main(void)
{
    printf("%d, ", (int) pow(10, 2));
    printf("%d", (int) pow(10, 2));
    return 0;
}

但是,在这个在线编译器上,输出与预期的一样:100, 100. 我不知道是什么导致了这种行为。有什么想法吗?我的编程错误,编译器错误?

4

3 回答 3

6

对汇编代码的一些调查。(OllyDbg)

#include <stdio.h>
#include <math.h>

int main(void)
{
    int x1 = (int) pow(10, 2);
    int x2 = (int) pow(10, 2);
    printf("%d %d", x1, x2);
    return 0;
}

相关的组装部分:

FLD QWORD PTR DS:[402000]   // Loads 2.0 onto stack
SUB ESP,8
FSTP QWORD PTR SS:[ESP]
FLD QWORD PTR DS:[402008]   // Loads 10.0 onto stack
SUB ESP,8
FSTP QWORD PTR SS:[ESP]
CALL <JMP.&msvcrt.pow>      // Calls pow API
                            // Returned value 100.00000000000000000
...


FLDCW WORD PTR DS:[402042]  //   OH! LOOK AT HERE
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...

FLD QWORD PTR DS:[402010]   // Loads 2.0 onto stack
SUB ESP,8
FSTP QWORD PTR SS:[ESP]
FLD QWORD PTR DS:[402018]   // Loads 10.0 onto stack
SUB ESP,8
FSTP QWORD PTR SS:[ESP]
CALL <JMP.&msvcrt.pow>      // Calls pow API again
                            // Returned value 99.999999999999999990

两次调用生成的代码相同,但输出不同。我不知道为什么 tcc 放在FLDCW那里。但是两个不同值的主要原因是那条线。

在该行之前,圆形尾数精度控制位为 53 位(10),但在执行该行后(它加载 FPU 寄存器控制),它将设置为 64 位(11)。另一方面,舍入控制最接近的,因此结果为 99.999999999999999990。阅读更多...

 

在此处输入图像描述

 


解决方案:

使用(int)to 将 a强制转换float为 anint后,您应该预料到这个数字错误,因为这种强制转换会将 [0, 1) 之间的值截断为零。

假设 10 2是 99.9999999999。在那次演员之后,结果是99。

尝试在将结果转换为整数之前对其进行四舍五入,例如:

printf("%d", (int) (floor(pow(10, 2) + 0.5)) );

 

于 2013-08-24T18:57:54.633 回答
2

您在 tcc 中发现了一个错误。感谢那。补丁刚刚提交到存储库。它将包含在下一个版本中,但这可能需要一段时间。您当然可以拉取源代码并自己构建它。补丁在这里

http://repo.or.cz/w/tinycc.git/commitdiff/73faaea227a53e365dd75f1dba7a5071c7b5e541

于 2013-08-28T21:14:43.653 回答
0

似乎舍入方法可能会改变,因此需要 ASM 指令finit来重置 FPU。在 Windows 上的 FreeBASIC 中,我99.9999在第一次尝试时得到了平衡,所以我认为在第一次尝试后对你来说它会是一个一致的99.9999. (但我确实将这种未定义行为称为 C 运行时中的错误pow()。)

所以我的建议是不要进行四舍五入的转换。为避免此类问题,请使用,例如:

int x1 = (int)(pow(10, 2)+.5);

于 2013-08-24T20:27:59.387 回答