自制的pow()c ++

时间:2012-03-11 04:48:26

标签: c++ math

我正在阅读How can I write a power function myself?并且dan04给出的答案引起了我的注意,主要是因为我不确定fortran给出的答案,但我接受了并实现了这个:

#include <iostream>
using namespace std;
float pow(float base, float ex){
    // power of 0
    if (ex == 0){
        return 1;
    // negative exponenet
    }else if( ex < 0){
        return 1 / pow(base, -ex);
    // even exponenet
    }else if ((int)ex % 2 == 0){
        float half_pow = pow(base, ex/2);
        return half_pow * half_pow;
    //integer exponenet
    }else{
        return base * pow(base, ex - 1);
    }
}
int main(){
    for (int ii = 0; ii< 10; ii++){\
        cout << "pow(" << ii << ".5) = " << pow(ii, .5) << endl;
        cout << "pow(" << ii << ",2) = " << pow(ii,  2) << endl;
        cout << "pow(" << ii << ",3) = " << pow(ii,  3) << endl;
    }
}

虽然我不确定我是否正确翻译,因为所有给出.5作为指数的调用返回0.在答案中它声明它可能需要基于a^b = 2^(b * log2(a))的log2(x),但是我不确定是否会把它放进去,因为我不确定该放在哪里,或者我是否正在考虑这一点。

注意:我知道这可能是在数学库中定义的,但我不需要为一些函数添加整个数学库的所有费用。

编辑:有没有人知道分数指数的浮点实现? (我已经看过一个双重实现,但那是使用寄存器的技巧,我需要浮点数,并添加一个库只是为了做一个技巧我会更好,只包括数学库)

5 个答案:

答案 0 :(得分:15)

以下是powf实际实现的链接。

我希望更简单的解决方案在结果中缺乏准确性或不处理InF和NaN参数。

http://opensource.apple.com/source/Libm/Libm-2026/Source/Intel/expf_logf_powf.c

http://opensource.apple.com/source/Libm/Libm-315/Source/ARM/powf.c

答案 1 :(得分:7)

我查看了本文here,其中描述了如何近似指数函数以获得双精度。在对维基百科进行了一些关于单精度浮点表示的研究后,我得出了等效的算法。他们只实现了exp函数,所以我找到了日志的反函数,然后只做了

    POW(a, b) = EXP(LOG(a) * b).

编译这个gcc4.6.2产生的pow函数几乎比标准库的实现快4倍(用O2编译)。

注意:EXP的代码几乎是从我读过的论文中逐字复制的,LOG函数是从here复制的。

以下是相关代码:

    #define EXP_A 184
    #define EXP_C 16249 

    float EXP(float y)
    {
      union
      {
        float d;
        struct
        {
    #ifdef LITTLE_ENDIAN
          short j, i;
    #else
          short i, j;
    #endif
        } n;
      } eco;
      eco.n.i = EXP_A*(y) + (EXP_C);
      eco.n.j = 0;
      return eco.d;
    }

    float LOG(float y)
    {
      int * nTemp = (int*)&y;
      y = (*nTemp) >> 16;
      return (y - EXP_C) / EXP_A;
    }

    float POW(float b, float p)
    {
      return EXP(LOG(b) * p);
    }

你可以在这里做一些优化,或者也许这已经足够好了。 这是一个粗略的近似,但如果您对使用双重表示引入的错误感到满意,我想这将是令人满意的。

答案 2 :(得分:4)

我认为您可以尝试使用Taylor的系列来解决它, 检查一下。 http://en.wikipedia.org/wiki/Taylor_series

使用泰勒系列,您可以使用已知的结果(例如3 ^ 4)解决任何难以求解的计算,例如3 ^ 3.8。在这种情况下,你有 3 ^ 4 = 81所以

3 ^ 3.8 = 81 + 3.8 * 3(3.8 - 4)+ .. + ..等等取决于你的n有多大,你将得到更密切的问题解决方案。

答案 3 :(得分:3)

我认为您正在寻找的算法可能是'nth root'。初始猜测为1(对于k == 0):

#include <iostream>
using namespace std;


float pow(float base, float ex);

float nth_root(float A, int n) {
    const int K = 6;
    float x[K] = {1};
    for (int k = 0; k < K - 1; k++)
        x[k + 1] = (1.0 / n) * ((n - 1) * x[k] + A / pow(x[k], n - 1));
    return x[K-1];
}

float pow(float base, float ex){
    if (base == 0)
        return 0;
    // power of 0
    if (ex == 0){
        return 1;
    // negative exponenet
    }else if( ex < 0){
        return 1 / pow(base, -ex);
    // fractional exponent
    }else if (ex > 0 && ex < 1){
        return nth_root(base, 1/ex);
    }else if ((int)ex % 2 == 0){
        float half_pow = pow(base, ex/2);
        return half_pow * half_pow;
    //integer exponenet
    }else{
        return base * pow(base, ex - 1);
    }
}
int main_pow(int, char **){
    for (int ii = 0; ii< 10; ii++){\
        cout << "pow(" << ii << ", .5) = " << pow(ii, .5) << endl;
        cout << "pow(" << ii << ",  2) = " << pow(ii,  2) << endl;
        cout << "pow(" << ii << ",  3) = " << pow(ii,  3) << endl;
    }
    return 0;
}

试验:

pow(0, .5) = 0.03125
pow(0,  2) = 0
pow(0,  3) = 0
pow(1, .5) = 1
pow(1,  2) = 1
pow(1,  3) = 1
pow(2, .5) = 1.41421
pow(2,  2) = 4
pow(2,  3) = 8
pow(3, .5) = 1.73205
pow(3,  2) = 9
pow(3,  3) = 27
pow(4, .5) = 2
pow(4,  2) = 16
pow(4,  3) = 64
pow(5, .5) = 2.23607
pow(5,  2) = 25
pow(5,  3) = 125
pow(6, .5) = 2.44949
pow(6,  2) = 36
pow(6,  3) = 216
pow(7, .5) = 2.64575
pow(7,  2) = 49
pow(7,  3) = 343
pow(8, .5) = 2.82843
pow(8,  2) = 64
pow(8,  3) = 512
pow(9, .5) = 3
pow(9,  2) = 81
pow(9,  3) = 729

答案 4 :(得分:2)

我和我的朋友在使用OpenGL项目时遇到了类似的问题,而且在某些情况下math.h还不够。我们的教练也遇到了同样的问题,他告诉我们要分离整数和浮动部分的功率。例如,如果要计算x ^ 11.5,则可以计算sqrt(x ^ 115,10),这可能会得到更准确的结果。