Eficiente 10 elevado al doble de potencia
Muchas veces tengo que subir 10 a la potencia del doble.
¿Existe una forma más eficiente de hacer esto que con la biblioteca matemática pow(10,double)? Si importa, mis dobles siempre son negativos entre -5 y -11.
Supongo que pow (double, double) usa un algoritmo más general que el requerido para pow (10, double) y, por lo tanto, podría no ser el método más rápido. Dadas algunas de las respuestas a continuación, esa podría haber sido una suposición incorrecta.
En cuanto al por qué, es por interpolación logarítmica. Tengo una tabla de valores xey. Mi objeto tiene un valor x conocido (que casi siempre es un doble).
double Dbeta(struct Data *diffusion, double per){
double frac;
while(per>diffusion->x[i]){
i++;
}
frac = (per-diffusion->x[i-1])/(diffusion->x[i]-diffusion->x[i-1]);
return pow(10,log10DB[i-1] + frac * (log10DB[i]-log10DB[i-1]));
}
Esta función se llama muchas veces. Me han dicho que examine la elaboración de perfiles, así que eso es lo que haré primero.
Me acaban de decir que podría haber usado logaritmos naturales en lugar de base 10, lo cual obviamente es correcto. (Mi estupidez a veces me sorprende incluso a mí mismo).
Después de reemplazar todo con logaritmos naturales, todo funciona un poco más rápido. Con la creación de perfiles (que es una palabra nueva que aprendí hoy) descubrí que el 39% de mi código se gasta en la función exp, así que para aquellos que se preguntaban si de hecho era esta parte la que estaba bloqueando mi código, lo era.
Respuestas
Sí, la función pow es lenta (aproximadamente 50 veces el costo de multiplicar, para aquellos que piden puntos de referencia).
Mediante algunos trucos de log / exponentes, podemos expresar 10 ^ x como
10^x = exp(log(10^x)) = exp(x * log(10)).Entonces puede implementar 10 ^ x con
exp(x * M_LN10), que debería ser más eficiente que pow.Si la precisión doble no es crítica, use la versión flotante de la función
expf(opowf), que debería ser más eficiente que la versión doble.Si la precisión aproximada es correcta, calcule previamente una tabla sobre el rango [-5, -11] y realice una búsqueda rápida con interpolación lineal.
Algunos puntos de referencia (usando glibc 2.31):
Benchmark Time
---------------------------------
pow(10, x) 15.54 ns
powf(10, x) 7.18 ns
expf(x * (float)M_LN10) 3.45 ns
Porque pow(10.0, n)debería ser más rápido de configurar c = log(10.0), que puede calcular una vez, luego usar exp(c*n), que debería ser significativamente más rápido que pow(10.0, n)(que básicamente hace lo mismo internamente, excepto que estaría calculando log(10.0)una y otra vez en lugar de solo una vez). Más allá de eso, probablemente no haya mucho más que puedas hacer.