Cómo escribir log base (2) en c / c ++

98

¿Hay alguna forma de escribir la función de registro (base 2)?

El lenguaje C tiene 2 funciones integradas - >>

1. logque es base e.

2. log10base 10;

Pero necesito la función de registro de la base 2. Cómo calcular esto.

Russell
fuente
1
Para los cálculos del globo ocular, el logaritmo en base 2 es casi igual al logaritmo en base 10 más el logaritmo natural. Obviamente, es mejor escribir una versión más precisa (y más rápida) en un programa.
David Thornley
Para números enteros, puede hacer un bucle en el desplazamiento de bits a la derecha y detenerse cuando llegue a 0. El conteo del bucle es una aproximación del registro
Basile Starynkevitch

Respuestas:

198

Matemáticas simples:

    log 2 ( x ) = log y ( x ) / log y (2)

donde y puede ser cualquier cosa, que para las funciones de registro estándar es 10 o e .

Adam Crume
fuente
56

C99 tiene log2(así como log2fy log2lpara flotador y doble largo).

Matthew Flaschen
fuente
53

Si está buscando un resultado integral, puede simplemente determinar el conjunto de bits más alto en el valor y devolver su posición.

tomlogic
fuente
27
También hay un buen método de juego de bits para esto (tomado del Integer.highestOneBit(int)método de Java ):i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1);
Joey
38
... owhile (i >>= 1) { ++l; }
Lee Daniel Crocker
2
@Joey Eso funcionaría asumiendo que el entero tiene 32 bits de ancho, ¿no? Para 64 bits tendría un extra i>>32. Pero como Java solo tiene entradas de 32 bits, está bien. Para C / C ++, debe tenerse en cuenta.
Zoso
43
#define M_LOG2E 1.44269504088896340736 // log2(e)

inline long double log2(const long double x){
    return log(x) * M_LOG2E;
}

(la multiplicación puede ser más rápida que la división)

Logicray
fuente
1
Solo quería aclarar: usando las reglas de conversión de registros + el hecho de que log_2 (e) = 1 / log_e (2) -> obtenemos el resultado
Guy L
16
log2(int n) = 31 - __builtin_clz(n)
w00t
fuente
9

Como se indica en http://en.wikipedia.org/wiki/Logarithm :

logb(x) = logk(x) / logk(b)

Lo que significa que:

log2(x) = log10(x) / log10(2)
Patricio
fuente
11
Tenga en cuenta que puede calcular previamente log10 (2) para aumentar el rendimiento.
corsiKa
@Johannes: Dudo que el compilador precalcule log10 (2). El compilador no sabe que log10 devolverá el mismo valor cada vez. Por lo que sabe el compilador, log10 (2) podría devolver valores diferentes en llamadas sucesivas.
abelenky
@abelenky: Ok, lo retiro. Dado que el compilador nunca ve la fuente de la log()implementación, no lo hará. Culpa mía.
Joey
3
@abelenky: Dado que log10()es una función definida en el estándar C, el compilador es libre de tratarla "especialmente", incluyendo precalcular el resultado, que creo que fue la sugerencia de @Johannes.
caf
1
@CarlNorum: Acabo de verificar, y gcc 4.7 al menos reemplaza log10(2)con una constante.
caf
8

Si desea hacerlo rápido, puede usar una tabla de búsqueda como en Bit Twiddling Hacks (solo integer log2).

uint32_t v; // find the log base 2 of 32-bit v
int r;      // result goes here

static const int MultiplyDeBruijnBitPosition[32] = 
{
  0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
  8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};

v |= v >> 1; // first round down to one less than a power of 2 
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;

r = MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];

Además, debe echar un vistazo a los métodos incorporados de sus compiladores, como _BitScanReversecuál podría ser más rápido porque puede calcularse completamente en hardware.

Eche un vistazo también a los posibles duplicados ¿Cómo hacer un log2 () entero en C ++?

bkausbk
fuente
¿Por qué la tabla de multiplicar y buscar al final? ¿No podrías simplemente hacer (v + 1) que redondearía a la siguiente potencia de dos? Y luego, podría cambiar uno a la derecha para redondear hacia abajo a la siguiente potencia de 2.
Safayet Ahmed
@SafayetAhmed Describe cómo quieres encontrar el log2 de un número con ese método. No conozco una forma más fácil de obtener ese valor. Además de usar la aritmética anterior con la tabla de búsqueda, se puede usar un algoritmo iterativo / recursivo o usar hardware dedicado / incorporado para hacer el cálculo.
bkausbk
Digamos que los bits de una variable v de 32 bits están numerados del 0 (LSB) al N (MSB). Digamos que el bit de conjunto más significativo de v es n. ¿Sería correcto decir que n representa piso (log2 (v))? ¿No está interesado en encontrar n dado v?
Safayet Ahmed
Me di cuenta de que lo que describí le daría la potencia más baja más cercana de 2 y no el logaritmo real. La multiplicación y la búsqueda de tablas son para ir de la potencia de dos al logaritmo. Está desplazando el número 0x07C4ACDD a la izquierda en cierta cantidad. La cantidad que mueva a la izquierda dependerá de la potencia de dos. El número es tal que cualquier secuencia consecutiva de 5 bits es única. (0000 0111 1100 0100 0110 1100 1101 1101) le da las secuencias (00000) (00001) ... (11101). Dependiendo de qué tan a la izquierda se desplace, obtendrá uno de estos patrones de 5 bits. Luego búsqueda de tabla. Muy agradable.
Safayet Ahmed
3
log2(x) = log10(x) / log10(2)
el vacío
fuente
Vota a favor por la simplicidad, la claridad y la provisión de código basado en la información proporcionada por el OP.
yoyo
3
uint16_t log2(uint32_t n) {//but truncated
     if (n==0) throw ...
     uint16_t logValue = -1;
     while (n) {//
         logValue++;
         n >>= 1;
     }
     return logValue;
 }

Básicamente lo mismo que el de tomlogic .

Ustaman Sangat
fuente
1
Hay un par de cosas que no funcionan con esta solución, pero en general, esto es bueno si desea evitar los puntos flotantes. Está confiando en el desbordamiento para que esto funcione, ya que está inicializando un entero sin signo con -1. Esto podría solucionarse inicializándolo a 0 y luego devolviendo el valor - 1, asumiendo que verifica el caso 0, lo cual hace. El otro problema es que confía en que el bucle se detiene cuando n == 0, lo que debe indicar explícitamente. Aparte de eso, esto es genial si desea evitar los puntos flotantes.
Rian Quinn
2

Tienes que incluir math.h (C) o cmath (C ++) Por supuesto, ten en cuenta que tienes que seguir las matemáticas que conocemos ... solo números> 0.

Ejemplo:

#include <iostream>
#include <cmath>
using namespace std;

int main(){
    cout<<log2(number);
}
usuario2143904
fuente
2

Necesitaba tener más precisión que solo la posición del bit más significativo, y el microcontrolador que estaba usando no tenía biblioteca matemática. Descubrí que solo usar una aproximación lineal entre 2 ^ n valores para argumentos de valor entero positivo funcionaba bien. Aquí está el código:

uint16_t approx_log_base_2_N_times_256(uint16_t n)
{
    uint16_t msb_only = 0x8000;
    uint16_t exp = 15;

    if (n == 0)
        return (-1);
    while ((n & msb_only) == 0) {
        msb_only >>= 1;
        exp--;
    }

    return (((uint16_t)((((uint32_t) (n ^ msb_only)) << 8) / msb_only)) | (exp << 8));
}

En mi programa principal, necesitaba calcular N * log2 (N) / 2 con un resultado entero:

temp = (((uint32_t) N) * approx_log_base_2_N_times_256) / 512;

y los valores de 16 bits nunca se desviaron más del 2%

David Reinagel
fuente
1

Todas las respuestas anteriores son correctas. Esta respuesta mía a continuación puede ser útil si alguien la necesita. He visto este requisito en muchas preguntas que estamos resolviendo usando C.

log2 (x) = logy (x) / logy (2)

Sin embargo, si está utilizando lenguaje C y desea que el resultado sea un número entero, puede utilizar lo siguiente:

int result = (int)(floor(log(x) / log(2))) + 1;

Espero que esto ayude.

Mazhar MIK
fuente
0

Consultar a las matemáticas básicas supuesto, log n / log 2. No importa si eliges logo, log10en este caso, dividir por el logde la nueva base funciona.

Pieter
fuente
0

Versión mejorada de lo que hizo Ustaman Sangat

static inline uint64_t
log2(uint64_t n)
{
    uint64_t val;
    for (val = 0; n > 1; val++, n >>= 1);

    return val;
}
Rian Quinn
fuente