2012-02-23 19 views
5

मुझे सी ++ में लॉग 2 (फ्लोट एक्स) फ़ंक्शन का एक बहुत तेज़ कार्यान्वयन चाहिए।फास्ट लॉग 2 (फ्लोट एक्स) कार्यान्वयन सी ++

मैं एक बहुत ही दिलचस्प कार्यान्वयन पाया (और बहुत तेज!)

#include <intrin.h> 

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

लेकिन इस समारोह केवल इनपुट में पूर्णांक मूल्यों के लिए अच्छा है।

प्रश्न: वहाँ किसी भी तरह से डबल प्रकार इनपुट चर को यह समारोह कन्वर्ट करने के लिए है?

युपीडी:

मैं इस कार्यान्वयन पाया:

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 के साथ शायद ही कभी कुछ के लिए बिट्स की संख्या की गणना के अलावा कुछ के लिए प्रयोग किया जाता है और जब आप बिट्स गिनते हैं तो आप पूर्णांक के साथ काम करते हैं। तो आपको इसके लिए क्या चाहिए? –

+2

@JanHudec: मेरे सिर के ऊपर से, लॉगरिदम के दो सामान्य उपयोग सिग्नल की एन्ट्रॉपी की गणना करेंगे, और बहुत बड़ी संख्याओं पर अंकगणित करेंगे जो अन्यथा ओवरफ्लो होगा। –

+0

@ माइकसेमोर: सिग्नल के लिए, पूर्णांक की बजाय फ़्लोटिंग पॉइंट होना दुर्लभ है। बड़ी संख्या में अंकगणितीय के लिए, आपको आधार 2 की आवश्यकता नहीं होगी और संभवतः प्राकृतिक लघुगणक का उपयोग करें क्योंकि गणित आमतौर पर इसके साथ व्यक्त किया जाता है। –

उत्तर

-1

यह फ़ंक्शन सी ++ नहीं है, यह एमएसवीसी ++ विशिष्ट है। इसके अलावा, मुझे बहुत संदेह है कि इस तरह के किसी भी अंतर्निहित मौजूद हैं। और यदि उन्होंने किया, तो मानक फ़ंक्शन को बस इसका उपयोग करने के लिए कॉन्फ़िगर किया जाएगा। तो बस मानक-प्रदान की गई लाइब्रेरी को कॉल करें।

-1

नहीं, लेकिन अगर आपको केवल परिणाम के पूर्णांक हिस्से की आवश्यकता है और पोर्टेबिलिटी पर जोर नहीं देते हैं, तो यहां तक ​​कि तेज भी है। क्योंकि आपको फ्लोट के एक्सपोनेंट हिस्से को निकालने की ज़रूरत है!

4

यदि आपको केवल लॉगरिदम के पूर्णांक भाग की आवश्यकता है, तो आप इसे सीधे फ़्लोटिंग पॉइंट नंबर से निकाल सकते हैं।

portably:

#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

आपका मतलब है कि यह फ्लोट या डबल के आईईईई कार्यान्वयन पर निर्भर करता है? बेशक लाइब्रेरी कार्यान्वयनकर्ताओं ने भी उस बारे में सोचा होगा? – CashCow

+0

@CashCow: दरअसल, और यह उम्मीद के अनुसार 'reinterpret_cast' काम पर भी निर्भर करता है - यह अनिर्धारित व्यवहार है। 'frexp' निश्चित रूप से आईईईई प्रतिनिधित्व का लाभ उठाएगा; लेकिन जब तक लाइब्रेरी इनलाइन प्रदान नहीं करती है, तब भी एक फ़ंक्शन कॉल की लागत लगती है, और महत्व को निकाला जाता है। बुरा संस्करण उन चीजों को नहीं करता है। 'Frexp' के लिए –

+2

+1। –

2

आप this implementation में एक बार देख ले सकते हैं, लेकिन:

  • यह नहीं हो सकता कुछ प्लेटफॉर्म पर काम
  • शायद 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

ऐसा लगता है कि यह काम नहीं करता है। जीसीसी-4.4.7 के साथ संकलित, fast_log2 (1024.f) रिटर्न -347469। – netvope

+0

https://github.com/romeric/fastapprox में एक ही परिशुद्धता पर एक बहुत तेज़ फ़ंक्शन है, या लगभग उच्च परिशुद्धता – Job

+0

के साथ लगभग-साथ-तेज़ फ़ंक्शन इस शीर्षलेख में विशेष रूप से: https://github.com/romeric/ fastapprox/ब्लॉब/मास्टर/fastapprox/src/fastlog।एच – Job

4

MSVC + जीसीसी संगत संस्करण है कि 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

थोड़ा अधिक सटीक फॉर्मूला (अधिकतम त्रुटि ± 0.00493976), [रीमेज़ 'एल्गोरिदम] का उपयोग करके अनुकूलित (http://en.wikipedia.org/wiki/Approximation_theory#Remez.27_algorithm): '((-0.34484843f) * u। वैल + 2.02466578 एफ) * u.val - 0.67487759f' – netvope

+0

@netvope गंभीरता से इस टिप्पणी के लिए धन्यवाद, मुझे अपनी आंखें खोलने और सन्निकटन सिद्धांत सीखने के लिए धन्यवाद! – nimig18

0

यह पहला जवाब है जो आईईईई कार्यान्वयन पर भरोसा नहीं करता है, हालांकि मुझे लगता है कि यह आईईईई मशीनों पर केवल तेज़ है जहां 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"; 
    } 
} 
संबंधित मुद्दे