So schreiben Sie die Protokollbasis (2) in c / c ++

97

Gibt es eine Möglichkeit, die Protokollfunktion (Basis 2) zu schreiben?

Die C-Sprache hat 2 eingebaute Funktionen - >>

1. logwelches ist Basis e.

2. log10Basis 10;

Aber ich brauche die Log-Funktion von Basis 2.Wie man das berechnet.

Russell
quelle
1
Für Augapfelberechnungen entspricht der Logarithmus zur Basis 2 nahezu dem Logarithmus zur Basis 10 plus dem natürlichen Logarithmus. Offensichtlich ist es besser, eine genauere (und schnellere) Version in ein Programm zu schreiben.
David Thornley
Für ganze Zahlen könnten Sie bei rechter Bitverschiebung eine Schleife ausführen und bei Erreichen von 0 anhalten. Die Anzahl der Schleifen ist eine Annäherung an das Protokoll
Basile Starynkevitch

Antworten:

197

Einfache Mathematik:

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

Dabei kann y alles sein, was für Standardprotokollfunktionen entweder 10 oder e ist .

Adam Crume
quelle
55

C99 hat log2(sowie log2fund log2lfür Float und Long Double).

Matthew Flaschen
quelle
52

Wenn Sie nach einem ganzzahligen Ergebnis suchen, können Sie einfach das höchste im Wert gesetzte Bit bestimmen und seine Position zurückgeben.

Tomlogic
quelle
26
Es gibt auch eine nette Bit-Twiddling-Methode dafür (entnommen aus Javas Integer.highestOneBit(int)Methode):i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1);
Joey
37
... oderwhile (i >>= 1) { ++l; }
Lee Daniel Crocker
2
@Joey Das würde funktionieren, wenn die Ganzzahl 32 Bit breit ist, oder? Für 64 Bit hätte es ein Extra i>>32. Da Java jedoch nur 32-Bit-Ints hat, ist dies in Ordnung. Für C / C ++ muss dies berücksichtigt werden.
Zoso
42
#define M_LOG2E 1.44269504088896340736 // log2(e)

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

(Multiplikation kann schneller sein als Division)

Logicray
quelle
1
Ich wollte nur klarstellen - unter Verwendung der Protokollkonvertierungsregeln + der Tatsache, dass log_2 (e) = 1 / log_e (2) -> wir erhalten das Ergebnis
Guy L
15
log2(int n) = 31 - __builtin_clz(n)
w00t
quelle
9

Wie auf http://en.wikipedia.org/wiki/Logarithm angegeben :

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

Was bedeutet, dass:

log2(x) = log10(x) / log10(2)
Patrick
quelle
11
Beachten Sie, dass Sie log10 (2) vorberechnen können, um die Leistung zu steigern.
CorsiKa
@Johannes: Ich bezweifle, dass der Compiler log10 (2) vorberechnet. Der Compiler weiß nicht, dass log10 jedes Mal den gleichen Wert zurückgibt. Nach allem, was der Compiler weiß, kann log10 (2) bei aufeinanderfolgenden Aufrufen unterschiedliche Werte zurückgeben.
Abelenky
@abelenky: Ok, das nehme ich zurück. Da der Compiler die Quelle für die log()Implementierung nie sieht , wird er dies nicht tun. Mein Fehler.
Joey
3
@abelenky: Da log10()eine Funktion im C-Standard definiert ist, kann der Compiler sie "speziell" behandeln, einschließlich der Vorberechnung des Ergebnisses, was meiner Meinung nach @Johannes 'Vorschlag war?
Café
1
@CarlNorum: Ich habe es gerade überprüft und gcc 4.7 wird zumindest log10(2)durch eine Konstante ersetzt.
Café
8

Wenn Sie es schnell machen möchten, können Sie eine Nachschlagetabelle wie in Bit Twiddling Hacks verwenden (nur Ganzzahl 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];

Darüber hinaus sollten Sie einen Blick auf Ihren Compiler builtin Methoden nehmen wie _BitScanReversedie könnten schneller sein , weil es vollständig in Hardware berechnet werden.

Schauen Sie sich auch mögliche Duplikate an. Wie erstelle ich eine Ganzzahl log2 () in C ++?

bkausbk
quelle
Warum die Multiplikation und Tabellensuche am Ende? Könnten Sie nicht einfach (v + 1) tun, was auf die nächste Zweierpotenz aufrunden würde? Und dann könnten Sie um eins nach rechts wechseln, um auf die nächste Potenz von 2
abzurunden
@SafayetAhmed Bitte beschreiben Sie, wie Sie mit dieser Methode das log2 einer Nummer finden möchten. Ich kenne keinen einfacheren Weg, um diesen Wert zu erreichen. Neben der Verwendung der obigen Arithmetik mit der Nachschlagetabelle kann ein iterativer / rekursiver Algorithmus oder die Verwendung dedizierter / integrierter Hardware für die Berechnung verwendet werden.
Bkausbk
Angenommen, die Bits einer 32-Bit-Variablen v sind von 0 (LSB) bis N (MSB) nummeriert. Angenommen, das höchstwertige gesetzte Bit von v ist n. Wäre es richtig zu sagen, dass n den Boden darstellt (log2 (v))? Bist du nicht daran interessiert, nur ein gegebenes v zu finden?
Safayet Ahmed
Mir wurde klar, dass das, was ich beschrieben habe, nur die niedrigste Potenz von 2 und nicht den tatsächlichen Logarithmus ergibt. Die Multiplikation und die Tabellensuche dienen dazu, von der Zweierpotenz zum Logarithmus zu gelangen. Sie verschieben die Zahl 0x07C4ACDD um einen gewissen Betrag nach links. Der Betrag, den Sie nach links verschieben, hängt von der Zweierpotenz ab. Die Nummer ist so, dass jede aufeinanderfolgende Folge von 5 Bits eindeutig ist. (0000 0111 1100 0100 0110 1100 1101 1101) gibt Ihnen die Sequenzen (00000) (00001) ... (11101). Je nachdem, wie weit Sie nach links verschieben, erhalten Sie eines dieser 5-Bit-Muster. Dann Tabellensuche. Sehr schön.
Safayet Ahmed
3
log2(x) = log10(x) / log10(2)
die Leere
quelle
Upvote für Einfachheit, Klarheit und Bereitstellung von Code basierend auf den gegebenen Informationen des OP.
Yooy
3
uint16_t log2(uint32_t n) {//but truncated
     if (n==0) throw ...
     uint16_t logValue = -1;
     while (n) {//
         logValue++;
         n >>= 1;
     }
     return logValue;
 }

Grundsätzlich das gleiche wie bei Tomlogic .

Ustaman Sangat
quelle
1
Es gibt ein paar Probleme mit dieser Lösung, aber im Allgemeinen ist dies hilfreich, wenn Sie Gleitkommazahlen vermeiden möchten. Sie verlassen sich auf einen Überlauf, damit dies funktioniert, da Sie eine vorzeichenlose Ganzzahl mit -1 initialisieren. Dies kann behoben werden, indem Sie es auf 0 initialisieren und dann den Wert - 1 zurückgeben, vorausgesetzt, Sie prüfen, ob Sie den Fall 0 haben. Das andere Problem ist, dass Sie sich darauf verlassen, dass die Schleife stoppt, wenn n == 0 ist, was Sie explizit angeben sollten. Abgesehen davon ist dies großartig, wenn Sie Gleitkommazahlen vermeiden möchten.
Rian Quinn
2

Sie müssen math.h (C) oder cmath (C ++) einschließen. Denken Sie natürlich daran, dass Sie die Mathematik befolgen müssen, die wir kennen ... nur Zahlen> 0.

Beispiel:

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

int main(){
    cout<<log2(number);
}
user2143904
quelle
2

Ich musste präziser sein als nur die Position des höchstwertigen Bits, und der von mir verwendete Mikrocontroller hatte keine Mathematikbibliothek. Ich fand heraus, dass nur die Verwendung einer linearen Näherung zwischen 2 ^ n-Werten für positive Ganzzahlwertargumente gut funktioniert. Hier ist der Code:

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

In meinem Hauptprogramm musste ich N * log2 (N) / 2 mit einem ganzzahligen Ergebnis berechnen:

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

und alle 16-Bit-Werte waren nie um mehr als 2% niedriger

David Reinagel
quelle
1

Alle obigen Antworten sind richtig. Diese Antwort von mir unten kann hilfreich sein, wenn jemand sie braucht. Ich habe diese Anforderung in vielen Fragen gesehen, die wir mit C lösen.

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

Wenn Sie jedoch die Sprache C verwenden und das Ergebnis in einer Ganzzahl anzeigen möchten, können Sie Folgendes verwenden:

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

Hoffe das hilft.

Mazhar MIK
quelle
0

Konsultieren Sie Ihren Grundkurs Mathematik log n / log 2. Es spielt keine Rolle, ob Sie wählen logoder log10in diesem Fall ist das Teilen durch logdie neue Basis der Trick.

Pieter
quelle
0

Verbesserte Version von 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
quelle