pow功能在这里发生了什么?

Cur*_*Guy 10 c gcc compiler-optimization pow gcc4.9

我在这里看到了各种各样的答案,描述了powC 中函数的奇怪行为.
但我在这里有一些不同的问题.

在下面的代码我初始化int x = pow(10,2)int y = pow(10,n) (int n = 2).

在第一种情况下,当我打印它显示的结果时100 ,在另一种情况下它出来了99.

我知道pow返回double它会在存储时被截断int,但我想问为什么输出会有所不同.

CODE1

#include<stdio.h>
#include<math.h>
int main()
{
     int n = 2;
     int x;
     int y;
     x = pow(10,2);   //Printing Gives Output 100   
     y = pow(10,n);   //Printing Gives Output 99


     printf("%d %d" , x , y);

}
Run Code Online (Sandbox Code Playgroud)

Output : 100 99

为什么输出结果不同.?

我的gcc版本是4.9.2

更新:

代码2

int main()
    {
         int n = 2;
         int x;
         int y;
         x = pow(10,2);   //Printing Gives Output 100   
         y = pow(10,n);   //Printing Gives Output 99
         double k = pow(10,2);
         double l = pow(10,n);


         printf("%d %d\n" , x , y);
         printf("%f %f\n" , k , l);

    }
Run Code Online (Sandbox Code Playgroud)

输出: 100 99
100.000000 100.000000

更新2 CODE1的汇编指令

生成的装配说明GCC 4.9.2使用gcc -S -masm=intel:

    .LC1:
    .ascii "%d %d\0"
    .text
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
    push    ebp
    mov ebp, esp
    and esp, -16
    sub esp, 48
    call    ___main
    mov DWORD PTR [esp+44], 2
    mov DWORD PTR [esp+40], 100      //Concerned Line
    fild    DWORD PTR [esp+44]
    fstp    QWORD PTR [esp+8]
    fld QWORD PTR LC0
    fstp    QWORD PTR [esp]
    call    _pow                    //Concerned Line
    fnstcw  WORD PTR [esp+30]
    movzx   eax, WORD PTR [esp+30]
    mov ah, 12
    mov WORD PTR [esp+28], ax
    fldcw   WORD PTR [esp+28]
    fistp   DWORD PTR [esp+36]
    fldcw   WORD PTR [esp+30]
    mov eax, DWORD PTR [esp+36]
    mov DWORD PTR [esp+8], eax
    mov eax, DWORD PTR [esp+40]
    mov DWORD PTR [esp+4], eax
    mov DWORD PTR [esp], OFFSET FLAT:LC1
    call    _printf
    leave
    ret
    .section .rdata,"dr"
    .align 8
LC0:
    .long   0
    .long   1076101120
    .ident  "GCC: (tdm-1) 4.9.2"
    .def    _pow;   .scl    2;  .type   32; .endef
    .def    _printf;    .scl    2;  .type   32; .endef
Run Code Online (Sandbox Code Playgroud)

tre*_*tcl 7

我知道pow返回double并且在int中存储时会被截断,但我想问为什么输出会有所不同.

首先,如果你还没有,你必须放弃浮点数以任何方式合理或可预测的想法.double接近实数,几乎你用a做的任何事情double都可能是实际结果的近似值.

也就是说,正如您所意识到的那样,pow(10, n)产生了一个类似的值99.99999999999997,这是一个精确到15个有效数字的近似值.然后你告诉它截断到小于那个的最大整数,所以它扔掉了大部分.

(旁白:很少有一个很好的理由,一个转换doubleint.喜欢的东西通常你应该或者格式化显示sprintf("%.0f", x),其不正确地舍入,或者使用floor功能,它可以处理,可能是出了浮点数范围的int.如果没有那些适合你的目的,就像货币或日期计算,可能你不应该使用浮点数都没有.)

这里有两件奇怪的事情.首先,为什么pow(10, n)不准确?10,2和100都可以精确表示为double.我能提供的最佳答案是你使用的C标准库有一个bug.(编译器和标准库,我假设是gcc和glibc,是根据不同的发布时间表和不同的团队开发的.如果pow返回不准确的结果,那可能是glibc中的错误,而不是gcc.)

在你的问题的评论中,amdn发现了一个glibc错误与FP舍入可能有关,另一个Q&A更详细地说明了为什么会发生这种情况以及它是如何违反C标准的.chux的答案也解决了这个问题.(C不需要实现IEEE 754,但即使它确实如此,pow也不需要使用正确的舍入.)我仍然称这是一个glibc错误,因为它是一个不受欢迎的属性.

(也可以想象,虽然不太可能,但处理器的FPU是错误的.)

第二,为什么pow(10, n)不同pow(10, 2)这个更容易.gcc优化了可以在编译时计算结果的函数调用,因此pow(10, 2)几乎肯定会被优化为100.0.如果查看生成的汇编代码,您将只能找到一个调用pow.

GCC手册,章节6.59描述了标准库函数可以通过这种方式(跟随链接的完整列表)进行处理:

其余功能用于优化目的.

除了具有库等价物的内置函数(例如下面讨论的标准C库函数)或扩展到库调用之外,GCC内置函数总是内联扩展,因此没有相应的入口点,并且它们的地址不能是获得.尝试在函数调用以外的表达式中使用它们会导致编译时错误.

[...]

ISO C90函数abort,abs,acos,asin,atan2,atan,calloc,ceil,cosh,cos,exit,exp,fabs,floor,fmod,fprintf,fputs,frexp,fscanf,isalnum,isalpha,iscntrl,isdigit, isgraph,islower,isprint,ispunct,isspace,isupper,isxdigit,tolower,toupper,labs,ldexp,log10,log,malloc,memchr,memcmp,memcpy,memset,modf,pow,printf,putchar,puts,scanf,sinh, sin,snprintf,sprintf,sqrt,sscanf,strcat,strchr,strcmp,strcpy,strcspn,strlen,strncat,strncmp,strncpy,strpbrk,strrchr,strspn,strstr,tanh,tan,vfprintf,vprintf和vsprintf 都被认为是内置的-in函数除非-fno-builtin指定(或-fno-builtin-function为单个函数指定).

所以看起来你可以用它来禁用这种行为-fno-builtin-pow.


Mal*_*ean -1

您欺骗了它,让它认为输入是真实的,因此它给出了一个近似答案,该答案恰好略低于 100,例如 99.999999,然后被截断为 99。