正在发生的事情在这里POW功能?正在发生的事情在这里POW功能?(What is happening

2019-05-12 08:30发布

我看到这里的各种答案描绘的奇怪的行为pow在C函数
但我有一些不同的东西要问在这里。

在下面的代码我已初始化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);

}

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);

    }

输出: 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

Answer 1:

我知道,战俘返回双倍,它被截断在INT存储,但我要问,为什么输出变成不同。

您必须首先,如果你还没有准备好,放弃的想法,浮点数以任何方式或合理可预见的自己。 double仅仅是接近真实的数字和几乎所有的东西你有做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的一个bug,而不是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功能中止,ABS,ACOS,ASIN,ATAN2,反正切,释放calloc,小区,COSH,COS,出口,进出口,晶圆厂,地板,FMOD,fprintf中,的fputs,frexp,的fscanf,字符isalnum,因而isalpha,iscntrl判断,ISDIGIT, isgraph,islower判断,isprint判断,ispunct判断,isspace为,isupper,isxdigit判断,tolower的,TOUPPER,实验室,ldexp,日志10,日志,malloc的,了memchr,memcmp,memcpy的,memset的,MODF, 战俘 ,printf的,的putchar,看跌期权,scanf函数,双曲正弦,罪的snprintf,sprintf的,开方,sscanf的,strcat的,和strchr,STRCMP,strcpy的,strcspn,strlen的,strncat函数,STRNCMP,strncpy()函数,strpbrk,strrchr,strspn,的strstr,正切,棕褐色,vfprintf,vprintf和vsprintf 都被认定为建-in功能除非-fno-builtin指定(或-fno-builtin-function为单个功能被指定)。

因此,它似乎可以禁用这种行为-fno-builtin-pow



Answer 2:

为什么出来是不同的输出。 ? (在更新的所附代码)

我们不知道这个是不同的。

当比较文本出来的int/double ,一定要打印double用足够的精度 ,看它是否是100.000000或只是接近 100.000000或十六进制删除所有疑问。

printf("%d %d\n" , x , y);
// printf("%f %f\n" , k , l);
// Is it the FP number just less than 100?
printf("%.17e %.17e\n" , k , l);  // maybe 9.99999999999999858e+01
printf("%a %a\n" , k , l);        // maybe 0x1.8ffffffffffff0000p+6

为什么出来是不同的输出。 ? (在原始代码中)

C不指定的最准确度<math.h>的功能。 以下是所有兼容的结果。

// Higher quality functions return 100.0
pow(10,2) --> 100.0   
// Lower quality and/or faster one may return nearby results
pow(10,2) --> 100.0000000000000142...
pow(10,2) --> 99.9999999999999857...

分配一个浮点(FP)数的int简单丢弃该馏分不管分数如何接近1.0

当转换到FP的整数,更好地控制转化率和轮应对轻微的计算差异。

// long int lround(double x);
long i = lround(pow(10.0,2.0));


Answer 3:

你不是第一个发现这一点。 这里有一个讨论的形式2013: POW()转换为整数时,意想不到的结果

我猜测,由TCC家伙产生的汇编代码,使第二值计算结果,该结果非常接近100像mikijov参与这历史性的文章中说,看起来像错误已得到修复后要舍去。



Answer 4:

正如其他人所说,代码2返回99由于浮点截断。 为什么代码1返回不同的和正确答案的原因是因为一个libc的优化。

当功率是一个小的正整数,它是更有效的作为重复乘法执行该操作。 更简单的路径删除舍入。 由于这是内联看不到正在进行的函数调用。



Answer 5:

你已经上当它误以为输入是真实的,所以它提供了一个大概的答案,这恰好是略低于100,如99.999999那么被截断为99。



文章来源: What is happening here in pow function?