2012-02-23 60 views
5

我需要在C++中快速實現log2(float x)函數。快速log2(float x)實現C++

我發現一個很有趣的實現(和極快!)

#include <intrin.h> 

inline unsigned long log2(int x) 
{ 
    unsigned long y; 
    _BitScanReverse(&y, x); 
    return y; 
} 

但是這個功能只適用於輸入整數值不錯。

問題:有沒有什麼辦法將這個函數轉換爲double type input variable?

UPD

我發現這個實現:

typedef unsigned long uint32; 
typedef long int32; 
static inline int32 ilog2(float x) 
{ 
    uint32 ix = (uint32&)x; 
    uint32 exp = (ix >> 23) & 0xFF; 
    int32 log2 = int32(exp) - 127; 

    return log2; 
} 

比前面的例子要快得多,但輸出是無符號的類型。

是否可以使這個函數返回一個類型?

在此先感謝!

+0

這是一個很奇怪的要求,因爲對數與基地2很少用於任何東西,除了計算位數的東西,當你計算位數時,你使用整數。那麼你需要什麼? – 2012-02-23 11:18:59

+2

@JanHudec:關於我的頭頂,對數的兩種常見用法是計算信號的熵,並對非常大的數字進行算術運算,否則將會溢出。 – 2012-02-23 11:32:30

+0

@MikeSeymour:對於信號來說,很少是浮點數而不是整數。對於大數的算術,你不需要基數2,並且可能使用自然對數,因爲數學通常用那個來表示。 – 2012-02-23 12:12:32

回答

-1

此函數不是C++,它是MSVC++特有的。此外,我非常懷疑存在這樣的內在因素。如果他們這樣做,標準功能將被簡單地配置爲使用它。所以只需調用Standard提供的庫。

-1

不,但是如果您只需要結果的整體部分並且不堅持可移植性,則更快。因爲你需要的只是提取浮點數的指數部分!

4

如果你只需要對數的整數部分,那麼你可以直接從浮點數中提取。

可移植性:

#include <cmath> 

int log2_fast(double d) { 
    int result; 
    std::frexp(d, &result); 
    return result-1; 
} 

可能更快,但依靠不明確的,不確定的行爲:

int log2_evil(double d) { 
    return ((reinterpret_cast<unsigned long long&>(d) >> 52) & 0x7ff) - 1023; 
} 
+1

你的意思是它依賴於浮點或雙精度的IEEE實現?當然,圖書館的執行者也可能想到那個? – CashCow 2012-02-23 11:30:57

+0

@CashCow:確實,它也依賴'reinterpret_cast'按照希望工作 - 這是未定義的行爲。 'frexp'肯定會利用IEEE的代表性;但除非庫提供它內聯,否則這也會導致函數調用的成本,並提取有效數。邪惡的版本不會做這些事情。 – 2012-02-23 11:34:12

+2

用於'frexp'的+1。 – 2012-02-23 11:34:27

2

你可以看看到this implementation,但:

  • 也可以不在某些平臺上工作
  • 可能n OT拍的std ::登錄
4

Fast log() function(5×大約快)

的也許對你的興趣。代碼在這裏工作;這並不是無限精確的。隨着代碼的網頁(該>已被刪除)上破,我會在這裏發佈:

inline float fast_log2 (float val) 
{ 
    int * const exp_ptr = reinterpret_cast <int *> (&val); 
    int   x = *exp_ptr; 
    const int  log_2 = ((x >> 23) & 255) - 128; 
    x &= ~(255 << 23); 
    x += 127 << 23; 
    *exp_ptr = x; 

    val = ((-1.0f/3) * val + 2) * val - 2.0f/3; // (1) 

    return (val + log_2); 
} 

inline float fast_log (const float &val) 
{ 
    return (fast_log2 (val) * 0.69314718f); 
} 
+0

看來這不起作用。用gcc-4.4.7編譯,fast_log2(1024.f)返回-347469。 – netvope 2015-04-25 17:26:26

+0

https://github.com/romeric/fastapprox在相同的精度下具有更快的功能,或者具有更高精度的幾乎一樣快的功能 – Job 2018-03-08 17:49:17

+0

特別在此標頭中:https://github.com/romeric/ fastapprox /斑點/主/ fastapprox/SRC/fastlog。h – Job 2018-03-08 17:49:37

4

MSVC + GCC兼容的版本,讓XX.XXXXXXX + -0.0054545

float mFast_Log2(float val) { 
    union { float val; int32_t x; } u = { val }; 
    register float log_2 = (float)(((u.x >> 23) & 255) - 128);    
    u.x &= ~(255 << 23); 
    u.x += 127 << 23; 
    log_2 += ((-0.3358287811f) * u.val + 2.0f) * u.val -0.65871759316667f; 
    return (log_2); 
} 
+1

使用[Remez'算法](http://en.wikipedia.org/wiki/Approximation_theory#Remez.27_algorithm):'((-0.34484843f)* u)對公式稍微更精確(最大誤差±0.00493976)。 val + 2.02466578f)* u.val - 0.67487759f' – netvope 2015-04-25 23:17:09

+0

@netvope非常感謝你的評論,讓我睜開眼睛,學習近似理論! – nimig18 2017-04-14 20:25:53

0

這是對第一個不依賴於IEEE實現的答案的改進,儘管我認爲它只在IEEE機器上很快,其中frexp()基本上是一個無成本功能。

除了丟棄frexp返回的分數之外,可以用它來線性內插。如果分數值爲正值,則分數值介於0.5和1.0之間,所以我們將分數值從0.0增加到1.0,並將其添加到指數中。

在實踐中,它看起來像這個快速評估是好的,大約5-10%,總是返回一個有點低的值。我確信通過調整2*比例因子可以使它變得更好。

#include <cmath> 

double log2_fast(double d) { 
    int exponent; 
    double fraction = std::frexp(d, &exponent); 
    return (result-1) + 2* (fraction - 0.5); 
} 

您可以驗證這是否是合理的快速逼近這一點:

#include <cmath> 

int main() 
{ 
    for(double x=0.001;x<1000;x+=0.1) 
    { 
     std::cout << x << " " << std::log2(x) << " " << log2_fast(x) << "\n"; 
    } 
}