Pregunta

¿Es posible calcular pow(10,x) en tiempo de compilación?

Tengo un procesador sin soporte de punto flotante y división entera lenta.Estoy intentando realizar tantos cálculos como sea posible en tiempo de compilación.Puedo acelerar dramáticamente una función en particular si paso ambas x y C/pow(10,x) como argumentos (x y C son siempre números enteros constantes, pero son constantes diferentes para cada llamada).Me pregunto si puedo hacer que estas llamadas a funciones sean menos propensas a errores introduciendo una macro que haga el 1/pow(10,x) ¿Automáticamente, en lugar de obligar al programador a calcularlo?

¿Existe algún truco de preprocesador?¿Puedo forzar al compilador a optimizar la llamada a la biblioteca?

¿Fue útil?

Solución

Puede utilizar la notación científica para los valores de punto que es parte del lenguaje C flotante. Se ve así:

e = 1.602E-19   // == 1.602 * pow(10, -19)

El número antes de la E (la 1.602e-19 tal vez de capital o pequeño double) es la parte de fracción donde como el (firmado) secuencia de dígitos después de la f es la parte exponente. Por defecto, el número es del tipo F, pero se puede adjuntar un sufijo de punto flotante (l, L, float o long double) si necesita un <=> <=> o una.

No recomendaría embalarlo semántica en una macro:

  1. No funcionará para las variables, los valores de punto flotante, etc.
  2. La notación científica es más fácil de leer.

Otros consejos

Hay muy pocos valores posibles antes de que int desbordamiento (o incluso mucho). Por el amor claridades, lo convierten en una mesa!

editar: Si está utilizando flotadores (parece que estás), entonces no, no va a ser posible llamar a la función pow () en tiempo de compilación sin escribir código que se ejecuta en el proceso de marca y da salida a los valores de un archivo (tal como un archivo de cabecera) que se compila entonces.

GCC hará esto en un nivel suficientemente alto de optimización (-O1 lo hace por mí). Por ejemplo:

#include <math.h>

int test() {
        double x = pow(10, 4);
        return (int)x;
}

Compila en -O1 -m32 a:

        .file   "test.c"
        .text
.globl test
        .type   test, @function
test:
        pushl   %ebp
        movl    %esp, %ebp
        movl    $10000, %eax
        popl    %ebp
        ret
        .size   test, .-test
        .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
        .section        .note.GNU-stack,"",@progbits

Esto funciona sin el elenco también - por supuesto, usted consigue una instrucción de carga de punto flotante allí, como el Linux ABI pasa valores de retorno de punto flotante en registros de FPU.

Puede hacerlo con Boost.Preprocessor:

http://www.boost.org/ doc / libs / 1_39_0 / libs / preprocesador / doc / index.html

Código:

#include <boost/preprocessor/repeat.hpp>

#define _TIMES_10(z, n, data) * 10
#define POW_10(n) (1 BOOST_PP_REPEAT(n, _TIMES_10, _))

int test[4] = {POW_10(0), POW_10(1), POW_10(2), POW_10(3)};

En realidad, al explotar el preprocesador de C, puedes lograr que calcule C pow(10, x) para cualquier real C e integral x.Observe que, como señaló @quinmars, C le permite usar sintaxis científica para expresar constantes numéricas:

#define myexp 1.602E-19   // == 1.602 * pow(10, -19)

para ser utilizado para constantes.Con esto en mente, y un poco de inteligencia, podemos construir una macro de preprocesador que tome C y x y combinarlos en una ficha de exponenciación:

#define EXP2(a, b) a ## b
#define EXP(a, b) EXP2(a ## e,b)
#define CONSTPOW(C,x) EXP(C, x)

Esto ahora se puede utilizar como un valor numérico constante:

const int myint = CONSTPOW(3, 4); // == 30000
const double myfloat = CONSTPOW(M_PI, -2); // == 0.03141592653

En realidad, usted tiene M4 que es una forma de pre-procesador más potente que el GCC. Una diferencia principal entre los dos es de GCC no es recursivo mientras que M4 es. Se hace posibles cosas como hacer aritmética en tiempo de compilación (y mucho más!). El ejemplo de código es lo que le gustaría hacer, ¿verdad? Lo hice voluminosos en una fuente de un archivo; pero yo suelo poner las definiciones de macros de M4 en archivos separados y sintonizar mis reglas Makefile. De esta manera, el código se mantiene desde feas definiciones intrusivos M4 en el código fuente en C que he hecho aquí.

$ cat foo.c
define(M4_POW_AUX, `ifelse($2, 1, $1, `eval($1 * M4_POW_AUX($1, decr($2)))')')dnl
define(M4_POW, `ifelse($2, 0, 1, `M4_POW_AUX($1, $2)')')dnl

#include <stdio.h>

int                     main(void)
{
  printf("2^0 = %d\n", M4_POW(2, 0));
  printf("2^1 = %d\n", M4_POW(2, 1));
  printf("2^4 = %d\n", M4_POW(2, 4));

  return 0;
}

La línea de comandos para compilar este ejemplo de código utiliza la capacidad de GCC y M4 para leer desde la entrada estándar.

$ cat foo.c | m4 - | gcc -x c -o m4_pow -
$ ./m4_pow
2^0 = 1
2^1 = 2
2^4 = 16

Espero que esta ayuda!

Las versiones recientes de GCC (alrededor de 4,3) añadido la capacidad de utilizar GMP y MPFR hacer algunas optimizaciones de tiempo de compilación mediante la evaluación de funciones más complejas que son constantes. Este enfoque deja su código simple y portátil, y confía en que el compilador para hacer el trabajo pesado.

Por supuesto, hay límites a lo que puede hacer. Aquí hay un enlace a la descripción en la lista de cambios , que incluye una lista de las funciones que son compatibles con este. 'Prisionero de guerra' es uno de ellos.

Si sólo tiene que utilizar el valor en el tiempo de compilación , utilice el notación científica como 1E2 para pow(10, 2)

Si desea rellenar los valores en tiempo de compilación y luego usarlos más tarde en tiempo de ejecución a continuación, sólo tiene que utilizar una tabla de consulta porque hay noreferrer sólo 23 diferentes potencias de 10 que son < em> exactamente representable en doble precisión

double POW10[] = {1., 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10,
1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};

Puede obtener grandes potencias de 10 en tiempo de ejecución de la tabla de búsqueda de arriba para obtener rápidamente el resultado sin necesidad de multiplicar por 10 y otra vez, pero el resultado es sólo un valor cercano a una potencia de 10 al igual que cuando se utiliza 10EX con X> 22

double pow10(int x)
{
   if (x > 22)
      return POW10[22] * pow10(x - 22);
   else if (x >= 0)
      return POW10[x];
    else
        return 1/pow10(-x);
}

Si no se necesita exponentes negativos entonces la rama final puede ser eliminado.

También puede reducir el tamaño de la tabla de búsqueda aún más si la memoria no es una limitación. Por ejemplo mediante el almacenamiento de sólo potencias pares de 10 y multiplicar por 10 cuando el exponente es impar, el tamaño de la tabla es ahora sólo un medio.

Por desgracia, no se puede utilizar el preprocesador precalcular llamadas a las bibliotecas. Si x es integral podría escribir su propia función, pero si se trata de un tipo de punto flotante que no ve ninguna buena manera de hacer esto.

repetición de bdonlan en el clavo pero tenga en cuenta que puede realizar casi cualquier optimización seleccionado en la caja de compilación siempre y cuando esté dispuesto a analizar y analizar el código en su propio preprocesador personalizado. Es una tarea trivial en la mayoría versión de Unix para anular las reglas implícitas que llaman el compilador para llamar a un paso personalizado de su propia antes de que llegue el compilador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top