Jak napisać log base (2) w c / c ++

99

Czy istnieje sposób na zapisanie funkcji dziennika (podstawa 2)?

Język C ma 2 wbudowane funkcje - >>

1. logktóra jest podstawą e.

2. log10podstawa 10;

Ale potrzebuję funkcji dziennika o podstawie 2. Jak to obliczyć.

Russell
źródło
1
W przypadku obliczeń gałki ocznej logarytm o podstawie 2 jest bliski równości logarytmowi o podstawie 10 plus logarytm naturalny. Oczywiście lepiej jest napisać w programie dokładniejszą (i szybszą) wersję.
David Thornley
W przypadku liczb całkowitych można było wykonać pętlę z przesunięciem w prawo i zatrzymać się po osiągnięciu 0. Liczba pętli jest przybliżeniem dziennika
Basile Starynkevitch

Odpowiedzi:

199

Prosta matematyka:

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

gdzie y może być dowolną wartością, co dla standardowych funkcji dziennika wynosi 10 lub e .

Adam Crume
źródło
56

C99 ma log2(jak również log2fi log2ldla float i long double).

Matthew Flaschen
źródło
53

Jeśli szukasz wyniku całkowego, możesz po prostu określić najwyższy bit ustawiony w wartości i zwrócić jego pozycję.

tomlogic
źródło
27
Jest na to również niezła metoda przekręcania bitów (zaczerpnięta z Integer.highestOneBit(int)metody Javy ):i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1);
Joey
38
... lubwhile (i >>= 1) { ++l; }
Lee Daniel Crocker
2
@Joey To działałoby przy założeniu, że liczba całkowita ma 32 bity szerokości, nie? W przypadku wersji 64-bitowej miałby dodatkowy i>>32. Ale ponieważ Java ma tylko 32-bitowe inty, jest w porządku. W przypadku C / C ++ należy to wziąć pod uwagę.
Zoso
43
#define M_LOG2E 1.44269504088896340736 // log2(e)

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

(mnożenie może być szybsze niż dzielenie)

logicray
źródło
1
Chciałem tylko wyjaśnić - używając reguł konwersji logów + fakt, że log_2 (e) = 1 / log_e (2) -> otrzymujemy wynik
Guy L
17
log2(int n) = 31 - __builtin_clz(n)
w00t
źródło
9

Jak podano na http://en.wikipedia.org/wiki/Logarithm :

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

Co oznacza że:

log2(x) = log10(x) / log10(2)
Patrick
źródło
11
Zauważ, że możesz wstępnie obliczyć log10 (2), aby zwiększyć wydajność.
corsiKa
@Johannes: Wątpię, czy kompilator wstępnie obliczy log10 (2). Kompilator nie wie, że log10 za każdym razem zwróci tę samą wartość. Z tego, co wie kompilator, log10 (2) może zwracać różne wartości przy kolejnych wywołaniach.
abelenky
@abelenky: Ok, cofam to. Ponieważ kompilator nigdy nie widzi źródła log()implementacji, nie zrobi tego. Mój błąd.
Joey
3
@abelenky: Skoro log10()funkcja jest zdefiniowana w standardzie C, kompilator może traktować ją „specjalnie”, włączając w to wstępne obliczanie wyniku, co, jak sądzę, było sugestią @Johannes?
kawiarnia
1
@CarlNorum: Właśnie sprawdziłem i gcc 4.7 przynajmniej zastępuje log10(2)stałą.
kawiarnia
8

Jeśli chcesz zrobić to szybko, możesz użyć tabeli odnośników, takiej jak w Bit Twiddling Hacks (tylko liczba całkowita 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];

Ponadto powinieneś przyjrzeć się wbudowanym metodom kompilatorów, takim jak te, _BitScanReversektóre mogą być szybsze, ponieważ mogą być całkowicie obliczone sprzętowo.

Przyjrzyj się również możliwemu duplikatowi. Jak zrobić liczbę całkowitą log2 () w C ++?

bkausbk
źródło
Po co mnożenie i wyszukiwanie w tabeli na końcu? Nie mógłbyś po prostu zrobić (v + 1), które zaokrągliłoby w górę do następnej potęgi dwóch? Następnie możesz przesunąć się w prawo o jeden, aby zaokrąglić w dół do następnej potęgi 2.
Safayet Ahmed,
@SafayetAhmed Opisz, jak chcesz znaleźć log2 liczby za pomocą tej metody. Nie znam prostszego sposobu na uzyskanie tej wartości. Oprócz zastosowania powyższej arytmetyki z tablicą przeglądową, można użyć algorytmu iteracyjnego / rekurencyjnego lub użyć dedykowanego / wbudowanego sprzętu do wykonania obliczeń.
bkausbk
Powiedzmy, że bity 32-bitowej zmiennej v są ponumerowane od 0 (LSB) do N (MSB). Powiedzmy, że najbardziej znaczący zestaw bitów v to n. Czy poprawne byłoby stwierdzenie, że n reprezentuje piętro (log2 (v))? Czy nie jesteś zainteresowany znalezieniem n danego v?
Safayet Ahmed
Zdałem sobie sprawę, że to, co opisałem, da ci najbliższą najniższą potęgę 2, a nie rzeczywisty logarytm. Mnożenie i wyszukiwanie w tabeli służą do przechodzenia od potęgi dwójki do logarytmu. Przesuwasz liczbę 0x07C4ACDD w lewo o pewną kwotę. Wielkość przesunięcia w lewo zależy od potęgi dwóch. Liczba jest taka, że ​​każda kolejna sekwencja 5 bitów jest niepowtarzalna. (0000 0111 1100 0100 0110 1100 1101 1101) podaje sekwencje (00000) (00001) ... (11101). W zależności od tego, jak daleko w lewo się przesuniesz, otrzymasz jeden z tych 5-bitowych wzorów. Następnie przeszukaj tabelę. Bardzo dobrze.
Safayet Ahmed
3
log2(x) = log10(x) / log10(2)
the_void
źródło
Głosuj za prostotą, przejrzystością i dostarczaniem kodu w oparciu o podane informacje PO.
jojo
3
uint16_t log2(uint32_t n) {//but truncated
     if (n==0) throw ...
     uint16_t logValue = -1;
     while (n) {//
         logValue++;
         n >>= 1;
     }
     return logValue;
 }

Zasadniczo to samo, co tomlogic .

Ustaman Sangat
źródło
1
Jest kilka rzeczy nie tak z tym rozwiązaniem, ale ogólnie jest to przyjemne, jeśli chcesz uniknąć zmiennoprzecinkowych. Aby to zadziałało, polegasz na przepełnieniu, ponieważ inicjujesz liczbę całkowitą bez znaku z -1. Można to naprawić, inicjując go na 0, a następnie zwracając wartość - 1, zakładając, że sprawdzasz przypadek 0, co robisz. Inną kwestią jest to, że polegasz na zatrzymywaniu pętli, gdy n == 0, co powinieneś wyraźnie określić. Poza tym jest to świetne, jeśli chcesz uniknąć zmiennoprzecinkowych.
Rian Quinn
2

Musisz uwzględnić math.h (C) lub cmath (C ++). Oczywiście pamiętaj, że musisz postępować zgodnie z matematyką, którą znamy ... tylko liczby> 0.

Przykład:

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

int main(){
    cout<<log2(number);
}
user2143904
źródło
2

Musiałem mieć większą precyzję, niż tylko położenie najbardziej znaczącego bitu, a mikrokontroler, którego używałem, nie miał biblioteki matematycznej. Okazało się, że samo użycie liniowego przybliżenia między 2 ^ n wartościami dodatnich wartości całkowitych działa dobrze. Oto kod:

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

W moim głównym programie musiałem obliczyć N * log2 (N) / 2 z wynikiem w postaci liczby całkowitej:

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

a wszystkie wartości 16-bitowe nigdy nie były wyłączone o więcej niż 2%

David Reinagel
źródło
1

Wszystkie powyższe odpowiedzi są prawidłowe. Poniższa odpowiedź może być pomocna, jeśli ktoś jej potrzebuje. Widziałem ten wymóg w wielu pytaniach, które rozwiązujemy za pomocą C.

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

Jeśli jednak używasz języka C i chcesz, aby wynik był liczbą całkowitą, możesz użyć następującego:

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

Mam nadzieję że to pomoże.

Mazhar MIK
źródło
0

Konsultować swoje podstawowe matematyki Oczywiście log n / log 2. Nie ma znaczenia, czy wybierzesz, logczy log10w tym przypadku podzielenie przez lognową bazę załatwia sprawę .

Pieter
źródło
0

Ulepszona wersja tego, co zrobił 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
źródło