pow 函数中发生了什么? [英] What is happening here in pow function?

查看:24
本文介绍了pow 函数中发生了什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have seen various answer here that depicts Strange behavior of pow function in C.
But I Have something different to ask here.

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

In the below code I have initialized int x = pow(10,2) and int y = pow(10,n) (int n = 2).

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

In first case it when I print the result it shows 100 and in the other case it comes out to be 99.

我知道 pow 返回 double 并且它在存储在 int 中被截断,但我想问为什么输出变成不同的.

I know that pow returns double and it gets truncated on storing in int, but I want to ask why the output comes to be different.

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

}

输出:100 99

为什么输出结果不同.?

Why is the output coming out to be different. ?

我的 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
" , x , y);
         printf("%f %f
" , k , l);

    }

输出:100 99
100.000000 100.000000

更新 2 CODE1 的组装说明

使用gcc -S -masm=intel生成的汇编指令GCC 4.9.2:

    .LC1:
    .ascii "%d %d"
    .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

推荐答案

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

I know that pow returns double and it gets truncated on storing in int, but I want to ask why the output comes to be different.

如果您还没有,首先必须摒弃浮点数在任何方面都是合理或可预测的想法.double近似实数,并且几乎任何你对 double 所做的事情都可能是对实际结果的近似.

You must first, if you haven't already, divest yourself of the idea that floating-point numbers are in any way sensible or predictable. double only approximates real numbers and almost anything you do with a double is likely to be an approximation to the actual result.

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

That said, as you have realized, pow(10, n) resulted in a value like 99.99999999999997, which is an approximation accurate to 15 significant figures. And then you told it to truncate to the largest integer less than that, so it threw away most of those.

(另外:很少有好的理由将 double 转换为 int.通常您应该将其格式化以显示类似于 sprintf("%.0f", x),它可以正确进行四舍五入,或者使用 floor 函数,它可以处理可能超出的浮点数int 的范围.如果这些都不适合您的目的,例如货币或日期计算,您可能根本不应该使用浮点数.)

(Aside: there is rarely a good reason to convert a double to an int. Usually you should either format it for display with something like sprintf("%.0f", x), which does rounding correctly, or use the floor function, which can handle floating-point numbers that may be out of the range of an int. If neither of those suit your purpose, like in currency or date calculations, possibly you should not be using floating point numbers at all.)

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

There are two weird things going on here. First, why is pow(10, n) inaccurate? 10, 2, and 100 are all precisely representable as double. The best answer I can offer is that the C standard library you are using has a bug. (The compiler and the standard library, which I assume are gcc and glibc, are developed on different release schedules and by different teams. If pow is returning inaccurate results, that is probably a bug in glibc, not gcc.)

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

In the comments on your question, amdn found a glibc bug to do with FP rounding that might be related and another Q&A that goes into more detail about why this happens and how it's not a violation of the C standard. chux's answer also addresses this. (C doesn't require implementation of IEEE 754, but even if it did, pow isn't required to use correct rounding.) I will still call this a glibc bug, because it's an undesirable property.

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

(It's also conceivable, though unlikely, that your processor's FPU is wrong.)

第二,为什么 pow(10, n)pow(10, 2) 不同? 这个要简单得多.gcc 优化掉了可以在编译时计算结果的函数调用,所以 pow(10, 2) 几乎肯定会被优化到 100.0.如果查看生成的汇编代码,您会发现只有一个对 pow 的调用.

Second, why is pow(10, n) different from pow(10, 2)? This one is far easier. gcc optimizes away function calls for which the result can be calculated at compile time, so pow(10, 2) is almost certainly being optimized to 100.0. If you look at the generated assembly code, you will find only one call to pow.

GCC 手册第 6.59 节描述可以以这种方式处理哪些标准库函数(请查看完整列表的链接):

The GCC manual, section 6.59 describes which standard library functions may be treated in this way (follow the link for the full list):

剩下的功能是为了优化目的.

The remaining functions are provided for optimization purposes.

除了具有库等效项(例如下面讨论的标准 C 库函数)或扩展为库调用的内置函数外,GCC 内置函数始终是内联扩展的,因此没有相应的入口点及其无法获取地址.尝试在函数调用以外的表达式中使用它们会导致编译时错误.

With the exception of built-ins that have library equivalents such as the standard C library functions discussed below, or that expand to library calls, GCC built-in functions are always expanded inline and thus do not have corresponding entry points and their address cannot be obtained. Attempting to use them in an expression other than a function call results in a compile-time error.

[...]

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 都被识别为内置函数,除非指定了 -fno-builtin(或 -fno-builtin-function 是为单个函数指定).

The ISO C90 functions 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 and vsprintf are all recognized as built-in functions unless -fno-builtin is specified (or -fno-builtin-function is specified for an individual function).

因此您似乎可以使用 -fno-builtin-pow 禁用此行为.

So it would seem you can disable this behavior with -fno-builtin-pow.

这篇关于pow 函数中发生了什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆