2012-03-11 4 views
7

Я читал через How can I write a power function myself? и ответ дается dan04 привлекло мое внимание главным образом потому, что я не уверен, что ответ дается Fortran, но я взял, что и реализовано это:самодельных пау() C++

#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. В ответе он утверждает, что ему может понадобиться log2 (x) на основе a^b = 2^(b * log2(a)), но я не уверен, потому что я не уверен, где это сказать, или если я даже думаю об этом праве.

ПРИМЕЧАНИЕ: Я знаю, что это может быть определено в математической библиотеке, но мне не нужны все дополнительные затраты на всю математическую библиотеку для нескольких функций.

EDIT: кто-нибудь знает реализацию с плавающей запятой для дробных показателей? (Я видел двойную реализацию, но это использовало трюк с регистрами, и мне нужна плавающая точка, и добавление библиотеки просто для того, чтобы сделать трюк, мне было бы лучше, включая математическую библиотеку)

+3

Или дополнительные расходы из ФПУ? –

+2

вам не хватает дробного показателя (код для четного показателя) - глядя на исходную ссылку, я думаю, что вы копируете что-то, что поддерживает только числовые экспоненты (поэтому тесты для дробного не будут выполняться). –

+0

@ andrewcooke dang it. не могли бы вы помочь мне, указав необходимые изменения. – gardian06

ответ

14

Ниже приведены ссылки на реальные реализации 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

+0

+1 Я обнаружил, что это было нетривиально реализовать pow. Одна из проблем, что Ньютон-Рафсон для вычисления n-го корня сходится очень медленно (* не * очень быстро) для высоких n и x. Реализация, на которую ссылается этот ответ, похожа, по-видимому, только на 'float', позволяет избежать этого. –

+0

Это драгоценный камень, который искал последние 3 недели! Благодарим за сообщение! – nimig18

2

Я думаю алгоритм, который вы ищете, может быть «nth root». С начальным приближением 1 (при к == 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 
+0

У меня были другие проекты, над которыми нужно работать. это приводит к сбоям, когда дано сказать «.3» – gardian06

+0

Я попытался добавить .3, кажется, работает (я использую GCC 4.5). но см. редактирование для * base * == 0 ... – CapelliC

+0

Я попытался добавить 1/3 экспонента, значения искажены, я собираюсь проверить больше ... – CapelliC

6

Я смотрел на этой бумаге here, которая описывает, как аппроксимировать экспоненциальную функцию для двойной точности. После небольшого исследования в Википедии о представлении с плавающей точкой с одинарной точностью я разработал эквивалентные алгоритмы. Они только реализовали функцию ехра, так что я нашел обратную функцию для журнала, а затем просто сделали

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

Существует еще некоторые оптимизации вы можете сделать здесь, или, возможно, это достаточно хорошо. Это грубое приближение, но если бы вы были удовлетворены ошибками, введенными с использованием двойного представления, я предполагаю, что это будет удовлетворительным.

3

Я думаю, что вы могли бы попытаться решить его, используя серию Тейлора, . http://en.wikipedia.org/wiki/Taylor_series

С серией Тейлора вы можете решить любой трудный для решения расчет, такой как 3^3.8, используя уже известные результаты, такие как 3^4. В этом случае у вас есть 3^4 = 81, поэтому

3^3.8 = 81 + 3.8 * 3 (3.8 - 4) + .. + .. и так далее зависит от того, насколько велика ваша n, вы получите более близкое решение вашей проблемы.

+0

Серия Taylor подходит к вопросу о подгонке кривой и требует знания соответствующего количества терминов для использования для получения точного значения. для немногих, и вы можете легко достичь постоянного увеличения/уменьшения, а также для многих и ваших истощающих процессов. есть ли разумное предложение по числу k суммирования? – gardian06

+0

Вы можете сравнить результат последнего вычисления в серии с некоторым стандартным номером, например, 0,0000001, так что это предел точности. Таким образом, вы должны сделать это как итерацию как сумму и проверить расчет каждой итерации, если конечное значение этого значения составляет <0,000001, например, вы разбили итерацию. – AlexTheo

+0

Ряд Тейлора аппроксимируется только в одной точке, где в качестве минимаксного алгоритма, такого как (chebyshev, remez), находится над границей, что дает оптимальную равносильную погрешность. – nimig18

1

Я и мой друг столкнулись с подобной проблемой, пока мы работаем над проектом OpenGL, а в некоторых случаях недостаточно.У нашего преподавателя также была та же проблема, и он сказал нам, чтобы разделить мощность на целые и плавающие части. Например, если вы хотите рассчитать x^11.5, вы можете рассчитать sqrt (x^115, 10), что может привести к более точному результату.

+0

для этого потребуется более широкое возведение в степень. хотя это может упростить вычисление полномочий с плавающей запятой, это также увеличило бы количество рекурсивных или циклических вызовов, сделанных в пределах функции мощности (учитывая, что, если бит-математика не выполняется, ей нужен какой-то цикл) – gardian06

Смежные вопросы