Cómo calcular el registro con el preprocesador

¿Cómo hago log (x) con el preprocesador en Windows?

me gusta:

#define A log(4)/log(2) 

y después en mi código la matriz

 int b[A]; // A=2 will be computed with the preprocessor ! not in run time 

Bien, y ahora por el sucio engaño del preprocesador de fuerza bruta.

A partir de su pregunta, asumo que lo que realmente desea no es un logaritmo general (que ni siquiera es posible en la aritmética de enteros) sino el número de bits necesarios para representar un número dado. Si nos limitamos a los enteros de 32 bits, hay una solución para esto, aunque no es bonito.

 #define IS_REPRESENTIBLE_IN_D_BITS(D, N) \ (((unsigned long) N >= (1UL << (D - 1)) && (unsigned long) N < (1UL << D)) ? D : -1) #define BITS_TO_REPRESENT(N) \ (N == 0 ? 1 : (31 \ + IS_REPRESENTIBLE_IN_D_BITS( 1, N) \ + IS_REPRESENTIBLE_IN_D_BITS( 2, N) \ + IS_REPRESENTIBLE_IN_D_BITS( 3, N) \ + IS_REPRESENTIBLE_IN_D_BITS( 4, N) \ + IS_REPRESENTIBLE_IN_D_BITS( 5, N) \ + IS_REPRESENTIBLE_IN_D_BITS( 6, N) \ + IS_REPRESENTIBLE_IN_D_BITS( 7, N) \ + IS_REPRESENTIBLE_IN_D_BITS( 8, N) \ + IS_REPRESENTIBLE_IN_D_BITS( 9, N) \ + IS_REPRESENTIBLE_IN_D_BITS(10, N) \ + IS_REPRESENTIBLE_IN_D_BITS(11, N) \ + IS_REPRESENTIBLE_IN_D_BITS(12, N) \ + IS_REPRESENTIBLE_IN_D_BITS(13, N) \ + IS_REPRESENTIBLE_IN_D_BITS(14, N) \ + IS_REPRESENTIBLE_IN_D_BITS(15, N) \ + IS_REPRESENTIBLE_IN_D_BITS(16, N) \ + IS_REPRESENTIBLE_IN_D_BITS(17, N) \ + IS_REPRESENTIBLE_IN_D_BITS(18, N) \ + IS_REPRESENTIBLE_IN_D_BITS(19, N) \ + IS_REPRESENTIBLE_IN_D_BITS(20, N) \ + IS_REPRESENTIBLE_IN_D_BITS(21, N) \ + IS_REPRESENTIBLE_IN_D_BITS(22, N) \ + IS_REPRESENTIBLE_IN_D_BITS(23, N) \ + IS_REPRESENTIBLE_IN_D_BITS(24, N) \ + IS_REPRESENTIBLE_IN_D_BITS(25, N) \ + IS_REPRESENTIBLE_IN_D_BITS(26, N) \ + IS_REPRESENTIBLE_IN_D_BITS(27, N) \ + IS_REPRESENTIBLE_IN_D_BITS(28, N) \ + IS_REPRESENTIBLE_IN_D_BITS(29, N) \ + IS_REPRESENTIBLE_IN_D_BITS(30, N) \ + IS_REPRESENTIBLE_IN_D_BITS(31, N) \ + IS_REPRESENTIBLE_IN_D_BITS(32, N) \ ) \ ) 

La idea es que un número n > 0 tenga una representación que use exactamente d bits si y solo si n ≥ 2 d −1 y n <2 d . Después de tratar el caso n = 0 especialmente, simplemente forzamos esto para todas las 32 respuestas posibles.

La macro auxiliar IS_REPRESENTIBLE_IN_D_BITS(D, N) se expandirá a una expresión que evalúa a D si N puede representarse usando exactamente D bits y a -1 caso contrario. He definido las macros de modo que el resultado sea −1 si la respuesta es "no". Para compensar los sumndos negativos, agrego 31 al final. Si el número no se puede representar en ningún 1, ..., 32 bits, el resultado general será −1, lo que nos ayudará a detectar algunos errores.

La expresión BITS_TO_REPRESENT(42) es una constante de tiempo de comstackción válida para usar en una statement de longitud de matriz.

Dicho todo esto, el costo adicional por hacer que la matriz siempre tenga una longitud de 32 elementos parece aceptable para muchas aplicaciones y le ahorra algunos problemas. Así que solo usaría esos trucos si realmente tuviera que hacerlo.

Actualización: solo para evitar confusiones: esta solución no utiliza el preprocesador para evaluar el “logaritmo”. Todo lo que hace el preprocesador es realizar una sustitución de texto que puede ver si comstack con el conmutador -E (al menos para GCC). Echemos un vistazo a este código:

 int main() { int digits[BITS_TO_REPRESENT(42)]; return 0; } 

Será preprocesado (avisado):

 int main() { int digits[(42 == 0 ? 1 : (31 + (((unsigned long) 42 >= (1UL << (1 - 1)) && (unsigned long) 42 < (1UL << 1)) ? 1 : -1) + (((unsigned long) 42 >= (1UL << (2 - 1)) && (unsigned long) 42 < (1UL << 2)) ? 2 : -1) + (((unsigned long) 42 >= (1UL << (3 - 1)) && (unsigned long) 42 < (1UL << 3)) ? 3 : -1) + (((unsigned long) 42 >= (1UL << (4 - 1)) && (unsigned long) 42 < (1UL << 4)) ? 4 : -1) + (((unsigned long) 42 >= (1UL << (5 - 1)) && (unsigned long) 42 < (1UL << 5)) ? 5 : -1) + (((unsigned long) 42 >= (1UL << (6 - 1)) && (unsigned long) 42 < (1UL << 6)) ? 6 : -1) + (((unsigned long) 42 >= (1UL << (7 - 1)) && (unsigned long) 42 < (1UL << 7)) ? 7 : -1) + (((unsigned long) 42 >= (1UL << (8 - 1)) && (unsigned long) 42 < (1UL << 8)) ? 8 : -1) + (((unsigned long) 42 >= (1UL << (9 - 1)) && (unsigned long) 42 < (1UL << 9)) ? 9 : -1) + (((unsigned long) 42 >= (1UL << (10 - 1)) && (unsigned long) 42 < (1UL << 10)) ? 10 : -1) + (((unsigned long) 42 >= (1UL << (11 - 1)) && (unsigned long) 42 < (1UL << 11)) ? 11 : -1) + (((unsigned long) 42 >= (1UL << (12 - 1)) && (unsigned long) 42 < (1UL << 12)) ? 12 : -1) + (((unsigned long) 42 >= (1UL << (13 - 1)) && (unsigned long) 42 < (1UL << 13)) ? 13 : -1) + (((unsigned long) 42 >= (1UL << (14 - 1)) && (unsigned long) 42 < (1UL << 14)) ? 14 : -1) + (((unsigned long) 42 >= (1UL << (15 - 1)) && (unsigned long) 42 < (1UL << 15)) ? 15 : -1) + (((unsigned long) 42 >= (1UL << (16 - 1)) && (unsigned long) 42 < (1UL << 16)) ? 16 : -1) + (((unsigned long) 42 >= (1UL << (17 - 1)) && (unsigned long) 42 < (1UL << 17)) ? 17 : -1) + (((unsigned long) 42 >= (1UL << (18 - 1)) && (unsigned long) 42 < (1UL << 18)) ? 18 : -1) + (((unsigned long) 42 >= (1UL << (19 - 1)) && (unsigned long) 42 < (1UL << 19)) ? 19 : -1) + (((unsigned long) 42 >= (1UL << (20 - 1)) && (unsigned long) 42 < (1UL << 20)) ? 20 : -1) + (((unsigned long) 42 >= (1UL << (21 - 1)) && (unsigned long) 42 < (1UL << 21)) ? 21 : -1) + (((unsigned long) 42 >= (1UL << (22 - 1)) && (unsigned long) 42 < (1UL << 22)) ? 22 : -1) + (((unsigned long) 42 >= (1UL << (23 - 1)) && (unsigned long) 42 < (1UL << 23)) ? 23 : -1) + (((unsigned long) 42 >= (1UL << (24 - 1)) && (unsigned long) 42 < (1UL << 24)) ? 24 : -1) + (((unsigned long) 42 >= (1UL << (25 - 1)) && (unsigned long) 42 < (1UL << 25)) ? 25 : -1) + (((unsigned long) 42 >= (1UL << (26 - 1)) && (unsigned long) 42 < (1UL << 26)) ? 26 : -1) + (((unsigned long) 42 >= (1UL << (27 - 1)) && (unsigned long) 42 < (1UL << 27)) ? 27 : -1) + (((unsigned long) 42 >= (1UL << (28 - 1)) && (unsigned long) 42 < (1UL << 28)) ? 28 : -1) + (((unsigned long) 42 >= (1UL << (29 - 1)) && (unsigned long) 42 < (1UL << 29)) ? 29 : -1) + (((unsigned long) 42 >= (1UL << (30 - 1)) && (unsigned long) 42 < (1UL << 30)) ? 30 : -1) + (((unsigned long) 42 >= (1UL << (31 - 1)) && (unsigned long) 42 < (1UL << 31)) ? 31 : -1) + (((unsigned long) 42 >= (1UL << (32 - 1)) && (unsigned long) 42 < (1UL << 32)) ? 32 : -1) ) )]; return 0; } 

Esto se ve terrible y si se evaluara en tiempo de ejecución, sería una serie de instrucciones. Sin embargo, dado que todos los operandos son constantes (o literales, para ser precisos), el comstackdor puede evaluar esto en tiempo de comstackción. Tiene que hacerlo, porque una statement de longitud de matriz debe ser una constante en C 89.

Si está utilizando la macro en otros lugares que no requieren ser constantes en tiempo de comstackción, depende del comstackdor si evalúa la expresión o no. Sin embargo, debe esperarse que cualquier comstackdor razonable realice esta optimización bastante elemental, conocida como plegamiento constante , si las optimizaciones están habilitadas. En caso de duda, como siempre, eche un vistazo al código de ensamblaje generado.

Por ejemplo, consideremos este progtwig.

 int main() { return BITS_TO_REPRESENT(42); } 

La expresión en una statement de return claramente no requiere ser una constante de tiempo de comstackción, así que veamos qué código generará GCC. (Estoy usando el interruptor -S para detenerme en la etapa de ensamblaje).

Incluso sin ninguna optimización habilitada, obtengo el siguiente código de ensamblaje que muestra que la expansión de la macro se convirtió en la constante 6.

 main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $6, %eax # See the constant 6? popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc 

El #define preprocesador de C es puramente un mecanismo de sustitución de texto. No podrá calcular los valores de registro en el momento de la comstackción.

Es posible que puedas con las plantillas de C ++, pero eso es magia negra que no entiendo, y actualmente es irrelevante.

O como mencioné en un comentario a continuación, puede crear su propio preprocesador que evalúa las ecuaciones de tamaño de matriz antes de entregar el código actualizado al comstackdor C estándar.

Editar

Al hurgar un poco más, vi esta pregunta SO: ¿Alguno de los comstackdores C o C ++ optimiza dentro de las macros definidas?

Esta pregunta es acerca de evaluar esta cadena de macros:

 #include  #define ROWS 15 #define COLS 16 #define COEFF 0.15 #define NODES (ROWS*COLS) #define A_CONSTANT (COEFF*(sqrt(NODES))) 

El consenso fue que A_CONSTANT puede ser una constante de tiempo de comstackción, dependiendo de qué tan inteligente sea el comstackdor y qué funciones matemáticas se definen como intrinsics . También aludió a que GCC es lo suficientemente inteligente como para resolver esto en este caso.

Por lo tanto, la respuesta a su pregunta podría encontrarse al intentarlo y ver qué tipo de código produce realmente su comstackdor.

Una definición un poco más corta para la macro LOG trabaja con enteros de hasta 32 bits podría ser:

 #define LOG_1(n) (((n) >= 2) ? 1 : 0) #define LOG_2(n) (((n) >= 1<<2) ? (2 + LOG_1((n)>>2)) : LOG_1(n)) #define LOG_4(n) (((n) >= 1<<4) ? (4 + LOG_2((n)>>4)) : LOG_2(n)) #define LOG_8(n) (((n) >= 1<<8) ? (8 + LOG_4((n)>>8)) : LOG_4(n)) #define LOG(n) (((n) >= 1<<16) ? (16 + LOG_8((n)>>16)) : LOG_8(n)) 

Sin embargo, antes de usarlo, compruebe si realmente lo necesita. Las personas a menudo necesitan usar el logaritmo para los valores que son una potencia de 2. Por ejemplo, cuando se implementan matrices de bits o menos. Si bien es difícil calcular el log como una expresión constante, es muy fácil definir la potencia de 2. Por lo tanto, puede considerar definir sus constantes como:

 #define logA 4 #define A (1< 

en lugar de:

 #define A 16 #define logA LOG(A) 

Esta respuesta está inspirada en 5gon12eder , pero con una primera macro más simple. A diferencia de la solución de 5gon12eder , esta implementación da 0 para BITS_TO_REPRESENT(0) , lo que podría decirse que es correcto. Esta función BITS_TO_REPRESENT(N) devuelve el número de bits para representar un entero sin signo menor o igual que el entero no negativo N ; almacenar un número firmado de magnitud N necesitaría un bit adicional.

 #define NEEDS_BIT(N, B) (((unsigned long)N >> B) > 0) #define BITS_TO_REPRESENT(N) \ (NEEDS_BIT(N, 0) + NEEDS_BIT(N, 1) + \ NEEDS_BIT(N, 2) + NEEDS_BIT(N, 3) + \ NEEDS_BIT(N, 4) + NEEDS_BIT(N, 5) + \ NEEDS_BIT(N, 6) + NEEDS_BIT(N, 7) + \ NEEDS_BIT(N, 8) + NEEDS_BIT(N, 9) + \ NEEDS_BIT(N, 10) + NEEDS_BIT(N, 11) + \ NEEDS_BIT(N, 12) + NEEDS_BIT(N, 13) + \ NEEDS_BIT(N, 14) + NEEDS_BIT(N, 15) + \ NEEDS_BIT(N, 16) + NEEDS_BIT(N, 17) + \ NEEDS_BIT(N, 18) + NEEDS_BIT(N, 19) + \ NEEDS_BIT(N, 20) + NEEDS_BIT(N, 21) + \ NEEDS_BIT(N, 22) + NEEDS_BIT(N, 23) + \ NEEDS_BIT(N, 24) + NEEDS_BIT(N, 25) + \ NEEDS_BIT(N, 26) + NEEDS_BIT(N, 27) + \ NEEDS_BIT(N, 28) + NEEDS_BIT(N, 29) + \ NEEDS_BIT(N, 30) + NEEDS_BIT(N, 31) \ ) 

BITS_TO_REPRESENT es casi un logaritmo de base 2. Como la conversión predeterminada de punto flotante a entero es truncamiento, la versión entera de un logaritmo en base-2 corresponde al floor(log(N)/log(2)) cálculo de punto flotante floor(log(N)/log(2)) . BITS_TO_REPRESENT(N) devuelve uno mayor que floor(log(N)/log(2)) .

Por ejemplo:

  1. BITS_TO_REPRESENT(7) es 3 , mientras que floor(log(7)/log(2)) es 2 .
  2. BITS_TO_REPRESENT(8) es 4 , mientras que floor(log(8)/log(2)) es 3 .

Después de preprocesar su statement de matriz se verá así

 int b[log(4)/log(2)]; 

¿Es esa statement de matriz válida? No. En la statement de matriz, debe tener una expresión constante, y el log(4)/log(2) se computará en tiempo de ejecución.