Pregunta self hecho pow () c ++


Estaba leyendo a través de ¿Cómo puedo escribir una función de potencia yo mismo? y la respuesta dada por dan04 me llamó la atención principalmente porque no estoy seguro de la respuesta dada por fortran, pero la tomé e implementé esto:

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

aunque no estoy seguro si traduje este derecho porque todas las llamadas que dan .5 como el exponente devuelven 0. En la respuesta indica que podría necesitar un log2 (x) basado en a^b = 2^(b * log2(a)), pero no estoy seguro de poner eso ya que no estoy seguro de dónde ponerlo, o si estoy pensando en este derecho.

NOTA: Sé que esto podría definirse en una biblioteca matemática, pero no necesito todos los gastos adicionales de una biblioteca matemática completa para algunas funciones.

EDITAR: ¿alguien sabe una implementación de coma flotante para exponentes fraccionarios? (He visto una implementación doble, pero estaba usando un truco con los registros, y necesito un punto flotante, y al agregar una biblioteca solo para hacer un truco, sería mejor que solo incluyera la biblioteca matemática)


8
2018-03-11 04:48


origen


Respuestas:


A continuación hay enlaces a implementaciones reales de powf.

Espero que las soluciones más simples carezcan de precisión en el resultado o no manejen los parámetros InF y 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


15
2018-03-27 09:41



He visto este documento aquí que describe cómo aproximar la función exponencial para la precisión doble. Después de investigar un poco en Wikipedia sobre la representación de punto flotante de precisión simple, he resuelto los algoritmos equivalentes. Solo implementaron la función exp, así que encontré una función inversa para el registro y simplemente lo hice

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

compilando este gcc4.6.2 produce una función pow casi 4 veces más rápida que la implementación de la librería estándar (compilando con O2).

Nota: el código para EXP se copia casi textualmente del papel que leí y la función LOG se copia de aquí.

Aquí está el código relevante:

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

Todavía hay algo de optimización que puede hacer aquí, o tal vez eso es lo suficientemente bueno. Esta es una aproximación aproximada, pero si hubiera estado satisfecho con los errores introducidos con la doble representación, me imagino que esto será satisfactorio.


7
2018-03-22 15:00



Creo que podrías intentar resolverlo usando la serie de Taylor, Mira esto. http://en.wikipedia.org/wiki/Taylor_series

Con la serie de Taylor puede resolver cualquier cálculo difícil de resolver como 3 ^ 3.8 utilizando los resultados ya conocidos, como 3 ^ 4. En este caso tienes 3 ^ 4 = 81 entonces

3 ^ 3.8 = 81 + 3.8 * 3 (3.8 - 4) + .. + .. y así sucesivamente, dependiendo de qué tan grande sea tu n obtendrás una solución más cercana a tu problema.


4
2018-03-22 15:06



Creo que el algoritmo que estás buscando podría ser 'enésima raíz'. Con una estimación inicial de 1 (para 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;
}

prueba:

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

3
2018-03-13 09:48



Mi amigo y yo enfrentamos un problema similar mientras estamos en un proyecto de OpenGL y math.h no fue suficiente en algunos casos. Nuestro instructor también tuvo el mismo problema y nos dijo que separáramos el poder de las partes enteras y flotantes. Por ejemplo, si va a calcular x ^ 11.5, puede calcular sqrt (x ^ 115, 10) que puede dar como resultado resultados más precisos.


2
2018-03-24 20:33