C / c ++ 'da günlük tabanı (2) nasıl yazılır


99

Günlük (2 tabanı) işlevini yazmanın herhangi bir yolu var mı?

C dilinin 2 yerleşik işlevi vardır - >>

1. loge baz olan.

2. log10taban 10;

Ama 2 tabanının log fonksiyonuna ihtiyacım var, bunu nasıl hesaplayacağım.


1
Göz küresi hesaplamaları için, 2 tabanındaki logaritma, 10 tabanındaki logaritma artı doğal logaritmaya eşittir. Açıkçası, bir programda daha doğru (ve daha hızlı) bir sürüm yazmak daha iyidir.
David Thornley

Tamsayılar için, sağ bit kaydırmada döngü yapabilir ve 0'a ulaşıldığında durabilirsiniz. Döngü sayısı, günlüğün yaklaşık bir
değeridir

Yanıtlar:


202

Basit matematik:

    günlük 2 ( x ) = günlük y ( x ) / günlük y (2)

burada y herhangi bir şey olabilir, standart günlük işlevleri için 10 veya e'dir .



53

İntegral bir sonuç arıyorsanız, değerdeki en yüksek bit setini belirleyebilir ve konumunu döndürebilirsiniz.


27
Bunun için güzel bir bit döndürme yöntemi de var (Java'nın yönteminden alınmıştır Integer.highestOneBit(int)):i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1);
Joey

39
... veyawhile (i >>= 1) { ++l; }
Lee Daniel Crocker

2
@Joey Bu tam sayının 32 bit genişliğinde olduğunu varsayarsak işe yarar, değil mi? 64 bit için ekstra olacaktır i>>32. Ancak Java yalnızca 32 bitlik girişlere sahip olduğu için sorun değil. C / C ++ için dikkate alınması gerekir.
Zoso

43
#define M_LOG2E 1.44269504088896340736 // log2(e)

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

(çarpma, bölmeden daha hızlı olabilir)


1
Sadece açıklığa kavuşturmak istedim - günlük dönüştürme kurallarını kullanarak + log_2 (e) = 1 / log_e (2) -> sonucu alıyoruz
Guy L


9

Http://en.wikipedia.org/wiki/Logarithm adresinde belirtildiği gibi :

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

Bu şu anlama gelir:

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

11
Performansı artırmak için log10 (2) 'yi önceden hesaplayabileceğinizi unutmayın.
corsiKa

@Johannes: Derleyicinin log10 (2) 'yi önceden hesaplayacağından şüpheliyim. Derleyici, log10'un her seferinde aynı değeri döndüreceğini bilmiyor. Derleyicinin tüm bildiği için, log10 (2) ardışık çağrılarda farklı değerler döndürebilir.
abelenky

@abelenky: Tamam, bunu geri alıyorum. Derleyici, log()uygulamanın kaynağını asla görmediğinden, bunu yapmayacaktır. Benim hatam.
Joey

3
@abelenky: log10()C standardında tanımlanmış bir işlev olduğu için, derleyici, sonucun önceden hesaplanması da dahil olmak üzere "özel" olarak ele almakta özgürdür, bence @ Johannes'in önerisi?
CAF

1
@CarlNorum: Az önce kontrol ettim ve gcc 4.7 en azından log10(2)bir sabit ile değiştiriliyor .
caf

8

Hızlı yapmak istiyorsanız, Bit Twiddling Hacks'teki gibi bir arama tablosu kullanabilirsiniz (yalnızca tamsayı 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];

Ek olarak, derleyicilerinizin yerleşik yöntemlerine de bir göz atmalısınız, _BitScanReversehangisi daha hızlı olabilir, çünkü tamamen donanımda hesaplanabilir.

Olası kopyaya da bir göz atın C ++ 'da bir tamsayı log2 () nasıl yapılır?


Neden sonunda çarpma ve tablo araması? İkinin bir sonraki kuvvetine yuvarlayan (v + 1) 'i yapamaz mısın? Ve sonra, 2'nin bir sonraki gücüne geçmek için bir sağa
kayabilirsiniz

@SafayetAhmed Lütfen bu yöntemle bir sayının log2'sini nasıl bulmak istediğinizi açıklayın. Bu değeri elde etmenin daha kolay bir yolunu bilmiyorum. Arama tablosu ile yukarıdaki aritmetiği kullanmanın yanı sıra, hesaplama yapmak için yinelemeli / özyinelemeli bir algoritma veya adanmış / yerleşik donanım kullanılabilir.
bkausbk

32 bit değişken v'nin bitlerinin 0 (LSB) ile N (MSB) arasında numaralandırıldığını varsayalım. V'nin en anlamlı bitinin n olduğunu varsayalım. N'nin tabanı (log2 (v)) temsil ettiğini söylemek doğru olur mu? Sadece verilen n'yi bulmakla ilgilenmiyor musun?
Safayet Ahmed

Açıkladığım şeyin size gerçek logaritmayı değil, 2'nin en düşük kuvvetini vereceğini fark ettim. Çarpma ve tablo araması, ikinin kuvvetinden logaritmaya geçmek içindir. 0x07C4ACDD sayısını bir miktar sola kaydırıyorsunuz. Sola kaydırdığınız miktar ikinin gücüne bağlı olacaktır. Sayı öyledir ki, 5 bitlik herhangi bir ardışık dizi benzersizdir. (0000 0111 1100 0100 0110 1100 1101 1101) size (00000) [00001) ... [11101) dizilerini verir. Ne kadar sola kaydırdığınıza bağlı olarak, bu 5 bitlik modellerden birini elde edersiniz. Ardından tablo araması. Çok hoş.
Safayet Ahmed

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

Basitlik, açıklık ve OP'nin verdiği bilgilere dayalı olarak kod sağlama için oy verin.
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;
 }

Temelde tomlogic'inki ile aynı .


1
Bu çözümde yanlış olan birkaç şey var, ancak genel olarak, kayan noktalardan kaçınmak istiyorsanız bu güzel. -1 ile işaretsiz bir tamsayıyı başlattığınız için, bunun çalışması için taşmaya güveniyorsunuz. Bu, bunu 0 olarak başlatarak ve sonra - 1 değerini döndürerek düzeltilebilir, yaptığınız 0 durumunu kontrol ettiğinizi varsayarsak. Diğer sorun, n == 0 olduğunda, açıkça belirtmeniz gereken döngünün durdurulmasına güvenmenizdir. Bunun dışında, kayan noktalardan kaçınmak istiyorsanız bu harika.
Rian Quinn

2

Math.h (C) veya cmath (C ++) eklemelisiniz Elbette bildiğimiz matematiği takip etmeniz gerektiğini unutmayın ... sadece> 0 sayıları.

Misal:

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

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

2

Sadece en önemli bitin konumunun ve kullandığım mikrodenetleyicinin matematik kitaplığı olmadığı için daha fazla hassasiyete ihtiyacım vardı. Pozitif tamsayı değer bağımsız değişkenleri için sadece 2 ^ n değerleri arasında doğrusal bir yaklaşım kullanmanın işe yaradığını buldum. İşte 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));
}

Ana programımda, N * log2 (N) / 2 sonucunu bir tam sayı sonucuyla hesaplamam gerekiyordu:

temp = (((uint32_t) N) * yaklaşık_log_base_2_N_times_256) / 512;

ve 16 bit değerin tümü hiçbir zaman% 2'den fazla kapalı değildi


1

Yukarıdaki cevapların tümü doğrudur. Aşağıdaki cevabım, birinin ihtiyacı olursa yardımcı olabilir. Bu gereksinimi C kullanarak çözdüğümüz birçok soruda gördüm.

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

Ancak, C dilini kullanıyorsanız ve sonucun tamsayı olmasını istiyorsanız, aşağıdakileri kullanabilirsiniz:

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

Bu yardımcı olur umarım.


0

Temel matematik kursunuza danışın log n / log 2. Seçim yapıp yapmamanız önemli değil logveya log10bu durumda, logyeni üssün sayısına göre bölmek işe yarıyor.


0

Ustaman Sangat'ın yaptığı şeyin geliştirilmiş versiyonu

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

    return val;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.