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

jsj*_*jsj 42 c floating-point pow floating-point-precision

这里发生了什么:

#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));
}
Run Code Online (Sandbox Code Playgroud)

我得到这个输出:

17^12 = 582622237229761.000000
17^13 = 9904578032905936.000000
17^14 = 168377826559400928.000000
Run Code Online (Sandbox Code Playgroud)

13和14与wolfram alpa cf 不匹配:

12: 582622237229761.000000
    582622237229761

13: 9904578032905936.000000
    9904578032905937

14: 168377826559400928.000000
    168377826559400929
Run Code Online (Sandbox Code Playgroud)

而且,一些奇怪的部分并没有错 - 一个错了!

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

小智 65

powdouble数字一起使用.这些表示形式s*2 ^ e的数字,其中s是53位整数.因此double可存储的所有整数低于2 ^ 53,但只有一些以上整数2 ^ 53.特别是,它只能表示偶数> 2 ^ 53,因为对于e> 0,该值始终是2的倍数.

17 ^ 13需要54位来精确表示,因此e设置为1,因此计算值变为偶数.正确的值是奇数,因此它不会令人惊讶.同样,17 ^ 14需要58位来表示.它也是一个一个是幸运的巧合(只要你没有应用太多的数论),它恰好是32倍数中的一个,这是该double数量的数量的粒度圆润.

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

  • 一点模运算:17%16 = 1,因此17 ^ n%16 = 1(意味着17 ^ n的二进制表示中的四个最低有效位将始终为0001). (8认同)
  • @delnan我错过了17 ^ 14有五个截断位.但是17 ^ 2%32 = 1,所以17 ^ 14%32 =(17 ^ 2)^ 7%32 = 1. (5认同)
  • @ trideceth12有一个square-and-multiply算法,它在指数中具有运行时对数而不是线性. (5认同)
  • @tom这就是我的意思,通过应用太多的数论+1虽然它不是一个完整的解释,因为它需要是1(而不是17)模32被一个关闭.可能没有太多的模式,17 ^ 15不止一个. (3认同)
  • @ trideceth12这是你应该做的明显方式.并且构造重新发明轮子太简单了.鉴于你被限制在64位整数的范围内并且`x`和`y`是整数,既没有要解决的精度问题,也没有要求复杂算法的大性能挑战(10,20或甚至60乘法是便宜,比任何I/O便宜,例如). (2认同)

M O*_*ehm 14

你得到的数字太大,无法double准确表示.双精度浮点数基本上有53个有效二进制数字,可以表示最大为2^539,007,199,254,740,992的所有整数.

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

  • 啊......最接近的偶数是有道理的 (2认同)

bar*_*nos 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);
}
Run Code Online (Sandbox Code Playgroud)

迭代:

unsigned long long pow(unsigned long long x,unsigned int y)
{
    unsigned long long res = 1;
    while (y--)
        res *= x;
    return res;
}
Run Code Online (Sandbox Code Playgroud)

有效率的:

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 递归版本没用,因为它不重用答案.一次递归调用就足够了:`root = y?pow(x,y/2):1; return root*root*(y&1?x:1);` (9认同)