Come scrivere log in base (2) in c / c ++


98

C'è un modo per scrivere la funzione log (base 2)?

Il linguaggio C ha 2 funzioni integrate - >>

1. logche è la base e.

2. log10base 10;

Ma ho bisogno della funzione logaritmica di base 2. Come calcolarla.


1
Per i calcoli del bulbo oculare, il logaritmo in base 2 è quasi uguale al logaritmo in base 10 più il logaritmo naturale. Ovviamente è meglio scrivere una versione più accurata (e veloce) in un programma.
David Thornley

Per i numeri interi, è possibile eseguire il ciclo su bitshift destro e fermarsi quando si raggiunge lo 0. Il conteggio dei cicli è un'approssimazione del log
Basile Starynkevitch

Risposte:


198

Matematica semplice:

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

dove y può essere qualsiasi cosa, che per le funzioni di log standard è 10 o e .



53

Se stai cercando un risultato integrale, puoi semplicemente determinare il bit più alto impostato nel valore e restituirne la posizione.


27
C'è anche un bel metodo un po 'complicato per questo (preso dal Integer.highestOneBit(int)metodo Java ):i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1);
Joey

38
... oppurewhile (i >>= 1) { ++l; }
Lee Daniel Crocker il

2
@ Joey Funzionerebbe supponendo che il numero intero sia largo 32 bit, no? Per 64 bit avrebbe un extra i>>32. Ma poiché Java ha solo int a 32 bit, va bene. Per C / C ++ deve essere considerato.
Zoso

43
#define M_LOG2E 1.44269504088896340736 // log2(e)

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

(la moltiplicazione può essere più veloce della divisione)


1
Volevo solo chiarire - utilizzando le regole di conversione dei log + il fatto che log_2 (e) = 1 / log_e (2) -> otteniamo il risultato
Guy L

16
log2(int n) = 31 - __builtin_clz(n)

9

Come affermato su http://en.wikipedia.org/wiki/Logarithm :

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

Che significa che:

log2(x) = log10(x) / log10(2)

11
Notare che è possibile precalcolare log10 (2) per aumentare le prestazioni.
corsiKa

@ Johannes: dubito che il compilatore pre-calcola log10 (2). Il compilatore non sa che log10 restituirà ogni volta lo stesso valore. Per quanto ne sa il compilatore, log10 (2) potrebbe restituire valori diversi nelle chiamate successive.
abelenky

@abelenky: Ok, me lo riprendo. Poiché il compilatore non vede mai l'origine log()dell'implementazione, non lo farà. Colpa mia.
Joey

3
@abelenky: Poiché log10()è una funzione definita nello standard C, il compilatore è libero di trattarla "appositamente", incluso il precalcolo del risultato, che credo sia stato il suggerimento di @Johannes?
caf

1
@CarlNorum: ho appena controllato e gcc 4.7 almeno lo sostituisce log10(2)con una costante.
caf il

8

Se vuoi renderlo veloce, puoi usare una tabella di ricerca come in Bit Twiddling Hacks (solo log2 intero).

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];

Inoltre dovresti dare un'occhiata ai metodi incorporati del tuo compilatore come il _BitScanReversequale potrebbe essere più veloce perché può essere interamente calcolato in hardware.

Dai anche uno sguardo al possibile duplicato Come fare un intero log2 () in C ++?


Perché la moltiplicazione e la ricerca in tabella alla fine? Non potresti semplicemente fare (v + 1) che arrotonderebbe alla successiva potenza di due? E poi, potresti
spostarti a

@SafayetAhmed Descrivi come vuoi trovare il log2 di un numero con quel metodo. Non conosco un modo più semplice per ottenere quel valore. Oltre a utilizzare l'aritmetica sopra con la tabella di ricerca, è possibile utilizzare un algoritmo iterativo / ricorsivo o utilizzare hardware dedicato / incorporato per eseguire il calcolo.
bkausbk

Supponiamo che i bit di una variabile a 32 bit v siano numerati da 0 (LSB) a N (MSB). Supponiamo che il bit dell'insieme più significativo di v sia n. Sarebbe corretto dire che n rappresenta floor (log2 (v))? Non sei interessato a trovare solo n dato v?
Safayet Ahmed

Mi sono reso conto che ciò che ho descritto ti darebbe solo la potenza più bassa più vicina di 2 e non il logaritmo effettivo. La moltiplicazione e la ricerca in tabella servono per passare dalla potenza di due al logaritmo. Stai spostando il numero 0x07C4ACDD lasciato di una certa quantità. La quantità di spostamento a sinistra dipenderà dalla potenza di due. Il numero è tale che ogni sequenza consecutiva di 5 bit è unica. (0000 0111 1100 0100 0110 1100 1101 1101) fornisce le sequenze (00000) (00001) ... (11101). A seconda di quanto ci si sposta a sinistra, si ottiene uno di questi schemi a 5 bit. Quindi ricerca tabella. Molto bella.
Safayet Ahmed

3
log2(x) = log10(x) / log10(2)

Upvote per semplicità, chiarezza e fornitura di codice in base alle informazioni fornite dall'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;
 }

Fondamentalmente lo stesso di tomlogic .


1
Ci sono un paio di cose che non vanno in questa soluzione, ma in generale è utile se vuoi evitare i punti mobili. Ti affidi all'overflow perché funzioni poiché stai inizializzando un intero senza segno con -1. Questo potrebbe essere risolto inizializzandolo a 0 e quindi restituendo il valore - 1, assumendo che tu controlli il caso 0, cosa che fai. L'altro problema è che stai facendo affidamento sul fatto che il ciclo si interrompa quando n == 0, cosa che dovresti dichiarare esplicitamente. Oltre a questo, questo è ottimo se vuoi evitare i punti mobili.
Rian Quinn

2

Devi includere math.h (C) o cmath (C ++) Ovviamente tieni presente che devi seguire la matematica che conosciamo ... solo numeri> 0.

Esempio:

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

int main(){
    cout<<log2(number);
}

2

Avevo bisogno di avere più precisione che solo la posizione del bit più significativo e il microcontrollore che stavo usando non aveva una libreria matematica. Ho scoperto che il solo utilizzo di un'approssimazione lineare tra 2 ^ n valori per argomenti con valori interi positivi ha funzionato bene. Ecco il codice:

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

Nel mio programma principale, avevo bisogno di calcolare N * log2 (N) / 2 con un risultato intero:

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

e tutti i valori a 16 bit non sono mai stati off di oltre il 2%


1

Tutte le risposte precedenti sono corrette. Questa mia risposta di seguito può essere utile se qualcuno ne ha bisogno. Ho visto questo requisito in molte domande che stiamo risolvendo utilizzando C.

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

Tuttavia, se stai usando il linguaggio C e vuoi il risultato in numero intero, puoi usare quanto segue:

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

Spero che questo ti aiuti.


0

Consulta i tuoi matematica di base corso, log n / log 2. Non importa se scegli logo log10in questo caso, dividere per logla nuova base fa il trucco.


0

Versione migliorata di ciò che ha fatto Ustaman Sangat

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

    return val;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.