Cara menulis basis log (2) di c / c ++

99

Apakah ada cara untuk menulis fungsi log (basis 2)?

Bahasa C memiliki 2 fungsi bawaan - >>

1. logyang merupakan basis e.

2. log10basis 10;

Tapi saya membutuhkan fungsi log basis 2. Bagaimana menghitung ini.

russell
sumber
1
Untuk perhitungan bola mata, logaritma basis 2 mendekati sama dengan logaritma basis 10 ditambah dengan logaritma natural. Jelas lebih baik untuk menulis versi yang lebih akurat (dan lebih cepat) dalam sebuah program.
David Thornley
Untuk bilangan bulat, Anda dapat melakukan loop pada bitshift kanan, dan berhenti ketika mencapai 0. Jumlah loop adalah perkiraan dari log
Basile Starynkevitch

Jawaban:

199

Matematika sederhana:

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

di mana y bisa berupa apa saja, yang untuk fungsi log standar adalah 10 atau e .

Adam Crume
sumber
56

C99 memiliki log2(serta log2fdan log2luntuk float dan long double).

Matthew Flaschen
sumber
53

Jika Anda mencari hasil integral, Anda dapat menentukan kumpulan bit tertinggi dalam nilai dan mengembalikan posisinya.

tomlogic.dll
sumber
27
Ada juga metode bit-twiddling yang bagus untuk ini (diambil dari Integer.highestOneBit(int)metode Java ):i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1);
Joey
38
... atauwhile (i >>= 1) { ++l; }
Lee Daniel Crocker
2
@ Joey Itu akan bekerja dengan asumsi integer adalah 32 bit lebar, bukan? Untuk 64 bit itu akan memiliki ekstra i>>32. Tetapi karena Java hanya memiliki int 32-bit, itu baik-baik saja. Untuk C / C ++ perlu dipertimbangkan.
Zoso
43
#define M_LOG2E 1.44269504088896340736 // log2(e)

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

(perkalian mungkin lebih cepat dari pembagian)

logicray
sumber
1
Hanya ingin memperjelas - menggunakan aturan konversi log + fakta bahwa log_2 (e) = 1 / log_e (2) -> kami mendapatkan hasilnya
Guy L
17
log2(int n) = 31 - __builtin_clz(n)
w00t
sumber
9

Seperti yang dinyatakan di http://en.wikipedia.org/wiki/Logarithm :

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

Artinya:

log2(x) = log10(x) / log10(2)
Patrick
sumber
11
Perhatikan bahwa Anda dapat menghitung sebelumnya log10 (2) untuk meningkatkan kinerja.
corsiKa
@Johannes: Saya ragu kompilator akan menghitung log10 sebelumnya (2). Kompilator tidak tahu bahwa log10 akan mengembalikan nilai yang sama setiap saat. Untuk semua kompilator tahu, log10 (2) bisa mengembalikan nilai yang berbeda pada panggilan berturut-turut.
abelenky
@abelenky: Oke, saya ambil kembali. Karena kompilator tidak pernah melihat sumber log()implementasi , kompilator tidak akan melakukannya. Salahku.
Joey
3
@abelenky: Karena log10()fungsi didefinisikan dalam standar C, kompilator bebas untuk memperlakukannya "khusus", termasuk menghitung sebelumnya hasilnya, yang saya percaya adalah saran @Johannes?
caf
1
@CarlNorum: Saya baru saja memeriksa, dan gcc 4.7 setidaknya mengganti log10(2)dengan sebuah konstanta.
caf
8

Jika Anda ingin membuatnya cepat, Anda dapat menggunakan tabel pencarian seperti di Bit Twiddling Hacks (hanya 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];

Selain itu, Anda harus melihat metode builtin kompiler Anda seperti _BitScanReverseyang bisa lebih cepat karena dapat sepenuhnya dihitung dalam perangkat keras.

Perhatikan juga kemungkinan duplikat Bagaimana melakukan integer log2 () di C ++?

bkausbk.dll
sumber
Mengapa perkalian dan pencarian tabel di bagian akhir? Tidak bisakah Anda melakukan (v + 1) yang akan membulatkan ke pangkat dua berikutnya? Dan kemudian, Anda dapat menggeser ke kanan satu per satu untuk membulatkan ke pangkat 2. berikutnya
Safayet Ahmed
@SafayetAhmed Jelaskan bagaimana Anda ingin mencari log2 dari sebuah angka dengan metode itu. Saya tidak tahu cara yang lebih mudah untuk mendapatkan nilai itu. Selain menggunakan aritmatika di atas dengan tabel pencarian, seseorang dapat menggunakan algoritma iteratif / rekursif atau menggunakan perangkat keras khusus / bawaan untuk melakukan perhitungan.
bkausbk
Katakanlah bit dari variabel 32 bit v diberi nomor 0 (LSB) melalui N (MSB). Katakanlah set bit paling signifikan dari v adalah n. Apakah benar jika dikatakan bahwa n mewakili lantai (log2 (v))? Apakah Anda tidak tertarik hanya menemukan n diberikan v?
Safayet Ahmed
Saya menyadari bahwa apa yang saya jelaskan hanya akan memberi Anda pangkat 2 terendah terdekat dan bukan logaritma sebenarnya. Perkalian dan pencarian tabel untuk beralih dari pangkat dua ke logaritma. Anda menggeser jumlah 0x07C4ACDD yang tersisa. Jumlah yang Anda geser ke kiri akan bergantung pada kekuatan dua. Jumlahnya sedemikian rupa, sehingga setiap urutan 5 bit yang berurutan adalah unik. (0000 0111 1100 0100 0110 1100 1101 1101) memberikan Anda urutan (00000) (00001) ... (11101). Bergantung pada seberapa jauh Anda bergeser, Anda mendapatkan salah satu dari pola 5-bit ini. Kemudian pencarian tabel. Sangat bagus.
Safayet Ahmed
3
log2(x) = log10(x) / log10(2)
the_void
sumber
Beri suara positif untuk kesederhanaan, kejelasan, dan pemberian kode berdasarkan informasi yang diberikan 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;
 }

Pada dasarnya sama dengan tomlogic .

Ustaman Sangat
sumber
1
Ada beberapa hal yang salah dengan solusi ini, tetapi secara umum, ini bagus jika Anda ingin menghindari floating point. Anda mengandalkan overflow agar ini berfungsi karena Anda menginisialisasi integer unsigned dengan -1. Ini bisa diperbaiki dengan menginisialisasi ke 0 dan kemudian mengembalikan nilai - 1, dengan asumsi Anda memeriksa kasus 0, yang Anda lakukan. Masalah lainnya adalah Anda mengandalkan loop yang berhenti ketika n == 0, yang harus Anda nyatakan secara eksplisit. Selain itu, ini bagus jika Anda ingin menghindari floating point.
Rian Quinn
2

Anda harus memasukkan math.h (C) atau cmath (C ++) Tentu perlu diingat bahwa Anda harus mengikuti matematika yang kita tahu ... hanya angka> 0.

Contoh:

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

int main(){
    cout<<log2(number);
}
pengguna2143904
sumber
2

Saya perlu memiliki ketepatan yang lebih dari hanya posisi bit yang paling signifikan, dan mikrokontroler yang saya gunakan tidak memiliki perpustakaan matematika. Saya menemukan bahwa hanya menggunakan pendekatan linier antara nilai 2 ^ n untuk argumen nilai integer positif bekerja dengan baik. Ini kodenya:

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

Dalam program utama saya, saya perlu menghitung N * log2 (N) / 2 dengan hasil integer:

suhu = (((uint32_t) N) * perkiraan_log_base_2_N_times_256) / 512;

dan semua nilai 16 bit tidak pernah turun lebih dari 2%

David Reinagel
sumber
1

Semua jawaban di atas benar. Jawaban saya di bawah ini dapat membantu jika seseorang membutuhkannya. Saya telah melihat persyaratan ini dalam banyak pertanyaan yang kami selesaikan menggunakan C.

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

Namun, jika Anda menggunakan bahasa C dan ingin hasilnya berupa bilangan bulat, Anda dapat menggunakan yang berikut ini:

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

Semoga ini membantu.

Mazhar MIK
sumber
0

Konsultasikan kursus matematika dasar Anda log n / log 2,. Tidak masalah apakah Anda memilih logatau log10dalam hal ini, membagi dengan logpangkalan baru melakukan triknya.

Pieter
sumber
0

Versi perbaikan dari apa yang dilakukan 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
sumber