pow()似乎是一个人在这里

时间:2014-01-30 09:40:39

标签: c floating-point floating-point-precision pow

这里发生了什么:

#include <stdio.h>
#include <math.h>
int main(void) {
    printf("17^12 = %lf\n", pow(17, 12));
    printf("17^13 = %lf\n", pow(17, 13));
    printf("17^14 = %lf\n", pow(17, 14));
}

我得到了这个输出:

17^12 = 582622237229761.000000
17^13 = 9904578032905936.000000
17^14 = 168377826559400928.000000

13和14与wolfram alpa cf:

不匹配
12: 582622237229761.000000
    582622237229761

13: 9904578032905936.000000
    9904578032905937

14: 168377826559400928.000000
    168377826559400929

此外,一些奇怪的部分并没有错 - 只有一个是错的!

如果这是我达到pow()可以为我做的限制,那么有没有可以计算出来的替代方案?我需要一个可以计算x^y的函数,其中x^y总是小于ULLONG_MAX。

4 个答案:

答案 0 :(得分:65)

pow适用于double个数字。这些表示形式s * 2 ^ e的数字,其中s是53位整数。因此double可以将所有整数存储在2 ^ 53以下,但只有某些整数高于2 ^ 53。特别是,它只能表示偶数&gt; 2 ^ 53,因为对于e> 0值始终是2的倍数。

17 ^ 13需要54位来准确表示,因此e设置为1,因此计算出的值变为偶数。正确的值是奇数,因此它不会令人惊讶。同样,17 ^ 14需要58位来表示。这也是一个巧合的巧合(只要你没有应用太多的数论),它恰好是从 32的多个开始,这是粒度其中double个数量的四舍五入。

对于精确整数取幂,您应该一直使用整数。编写自己的double - 免费取幂程序。如果y可能很大,则通过平方使用取幂,但我认为它总是小于64,这使得这个问题没有实际意义。

答案 1 :(得分:14)

您获得的数字太大,无法准确地用double表示。双精度浮点数基本上包含53个有效二进制数字,可以表示最大2^53或9,007,199,254,740,992的所有整数。

对于更高的数字,最后的数字会被截断,计算结果将四舍五入到可以表示为double的下一个数字。对于仅略高于限制的17^13,这是最接近的偶数。对于大于2^54的数字,这是可被4整除的最接近的数字,依此类推。

答案 2 :(得分:14)

如果您的输入参数是非负整数,那么您可以实现自己的pow

<强>递归:

unsigned long long pow(unsigned long long x,unsigned int y)
{
    if (y == 0)
        return 1;
    if (y == 1)
        return x;
    return pow(x,y/2)*pow(x,y-y/2);
}

<强>迭代:

unsigned long long pow(unsigned long long x,unsigned int y)
{
    unsigned long long res = 1;
    while (y--)
        res *= x;
    return res;
}

<强>高效:

unsigned long long pow(unsigned long long x,unsigned int y)
{
    unsigned long long res = 1;
    while (y > 0)
    {
        if (y & 1)
            res *= x;
        y >>= 1;
        x *= x;
    }
    return res;
}

答案 3 :(得分:2)

对其他好答案的一个小补充:在x86架构下,通常有x87 80-bit extended format,大多数C编译器都通过long double类型支持。{{3}}。此格式允许使用最大2^64的整数运算,无间隙。

pow()中的<math.h>类似于long double个数字powl()。还应注意,long double值的格式说明符不是double值的格式说明符 - %Lf。所以使用long double类型的正确程序如下所示:

#include <stdio.h>
#include <math.h>
int main(void) {
    printf("17^12 = %Lf\n", powl(17, 12));
    printf("17^13 = %Lf\n", powl(17, 13));
    printf("17^14 = %Lf\n", powl(17, 14));
}

斯蒂芬·佳能在评论中指出,不能保证这个程序应该给出确切的结果。