जॉन कार्मैक का असामान्य तेज उलटा वर्गमूल (क्वेक III)


112

जॉन कार्मैक का क्वेक III स्रोत कोड में एक विशेष कार्य है जो फ्लोट के व्युत्क्रम वर्गमूल की गणना करता है (float)(1.0/sqrt(x)), जो एक 0x5f3759dfस्थिर स्थिरांक सहित नियमित रूप से 4x तेज है । नीचे दिए गए कोड को देखें। क्या कोई व्यक्ति लाइन से समझा सकता है कि वास्तव में यहाँ क्या चल रहा है और यह नियमित कार्यान्वयन की तुलना में बहुत तेज़ी से क्यों काम करता है?

float Q_rsqrt( float number )
{
  long i;
  float x2, y;
  const float threehalfs = 1.5F;

  x2 = number * 0.5F;
  y  = number;
  i  = * ( long * ) &y;
  i  = 0x5f3759df - ( i >> 1 );
  y  = * ( float * ) &i;
  y  = y * ( threehalfs - ( x2 * y * y ) );

  #ifndef Q3_VM
  #ifdef __linux__
    assert( !isnan(y) );
  #endif
  #endif
  return y;
}


10
यह कई बार अरबों के बारे में लिखा गया है। देखें: google.com/search?q=0x5f3759df
ग्रेग हेगिल

15
हालांकि धन्यवाद। यह "C # में एक सकारात्मक संख्या को नकारात्मक कैसे बनाते हैं?" की तुलना में बहुत अधिक रोचक प्रश्न था।
मुसिएनेसिस

9
कार्मैक नहीं था। en.wikipedia.org/wiki/Fast_inverse_square_root
h4xxr

7
पवित्र बकवास, यह सिर्फ एक हैक है जो न्यूटन की विधि पर आधारित है, इसकी एल्गोरिदम की कुछ पवित्र ग्रिल नहीं है, इसके बारे में बात करना बंद करो
दलील

जवाबों:


75

FYI करें। कार्मैक ने इसे नहीं लिखा। Terje Mathisen और Gary Tarolli दोनों इसके लिए आंशिक (और बहुत मामूली) क्रेडिट लेते हैं, साथ ही कुछ अन्य स्रोतों को भी श्रेय देते हैं।

कैसे पौराणिक स्थिरांक प्राप्त हुआ यह एक रहस्य है।

गैरी ट्रॉली को उद्धृत करने के लिए:

जो वास्तव में पूर्णांक में एक अस्थायी बिंदु गणना कर रहा है - यह पता लगाने में लंबा समय लगा कि यह कैसे और क्यों काम करता है, और मुझे अब विवरण याद नहीं है।

एक थोड़ा बेहतर निरंतर, जो एक विशेषज्ञ गणितज्ञ (क्रिस लोमोंट) द्वारा विकसित किया गया है , यह जानने के लिए कि मूल एल्गोरिदम ने कैसे काम किया है:

float InvSqrt(float x)
{
    float xhalf = 0.5f * x;
    int i = *(int*)&x;              // get bits for floating value
    i = 0x5f375a86 - (i >> 1);      // gives initial guess y0
    x = *(float*)&i;                // convert bits back to float
    x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy
    return x;
}

इसके बावजूद, उनका प्रारंभिक प्रयास आईडी के sqrt का गणितीय रूप से 'बेहतर' संस्करण है (जो लगभग एक ही स्थिर था) गणितीय रूप से बहुत 'शुद्ध' होने के बावजूद गैरी द्वारा विकसित किए गए एक से कमतर साबित हुआ। वह समझा नहीं सकता था कि आईडी इतना उत्कृष्ट iirc क्यों था।


4
"गणितीय रूप से शुद्ध" का क्या अर्थ है?
तारा

1
मुझे लगता है कि जहां पहला अनुमान न्यायसंगत स्थिरांक से प्राप्त किया जा सकता है, बजाय प्रतीत होता है कि मनमाना है। यद्यपि यदि आप एक तकनीकी विवरण चाहते हैं, तो आप इसे देख सकते हैं। मैं गणितज्ञ नहीं हूं, और गणितीय शब्दावली के बारे में एक शब्दार्थ चर्चा एसओ पर नहीं है।
रुशियो

7
यही कारण है कि मैंने इस शब्द को डराते हुए कहा, इस तरह के बकवास को रोकने के लिए। मुझे लगता है कि पाठक बोलचाल की अंग्रेजी लेखन से परिचित है, मुझे लगता है। आपको लगता है कि सामान्य ज्ञान पर्याप्त होगा। मैंने अस्पष्ट शब्द का उपयोग नहीं किया क्योंकि मुझे लगा कि "आप जानते हैं कि क्या है, मैं वास्तव में इस पर किसी ऐसे व्यक्ति द्वारा क्वियर होना चाहता हूं जिसे मूल स्रोत को देखने के लिए परेशान नहीं किया जा सकता है जो Google पर दो सेकंड लेगा"।
रुशियो जूल

2
खैर, आपने वास्तव में इस सवाल का जवाब नहीं दिया है।
BJovke

1
उन लोगों के लिए जो यह जानना चाहते हैं कि वह कहां पाता है: परे ३ डी
आर्टिकल्स

52

बेशक इन दिनों, यह केवल एफपीयू के sqrt (विशेष रूप से 360 / PS3 पर) का उपयोग करने की तुलना में बहुत धीमा हो जाता है, क्योंकि फ्लोट और इंट रजिस्टरों के बीच स्वैपिंग एक लोड-हिट-स्टोर को प्रेरित करता है, जबकि फ्लोटिंग पॉइंट यूनिट पारस्परिक वर्ग कर सकता है हार्डवेयर में जड़।

यह सिर्फ दिखाता है कि अंतर्निहित हार्डवेयर परिवर्तनों की प्रकृति के रूप में अनुकूलन को कैसे विकसित करना है।


4
यह अभी भी std :: sqrt () से बहुत अधिक तेज़ है।
तारा

2
क्या आपके पास कोई ज़रिया है? मैं रनटाइम्स का परीक्षण करना चाहता हूं लेकिन मेरे पास Xbox 360 डेवलपमेंट किट नहीं है।
DucRP

31

ग्रेग हेविल और इलिडान 4 ने उत्कृष्ट गणितीय स्पष्टीकरण के साथ एक लिंक दिया। मैं इसे उन लोगों के लिए यहाँ प्रस्तुत करने का प्रयास करूँगा जो विवरण में बहुत अधिक नहीं जाना चाहते हैं।

किसी भी गणितीय कार्य, कुछ अपवादों के साथ, एक बहुपद योग द्वारा दर्शाया जा सकता है:

y = f(x)

में तब्दील किया जा सकता है :

y = a0 + a1*x + a2*(x^2) + a3*(x^3) + a4*(x^4) + ...

जहाँ a0, a1, a2, ... स्थिरांक हैं । समस्या यह है कि कई कार्यों के लिए, वर्गमूल की तरह, सटीक मूल्य के लिए इस राशि के सदस्यों की अनंत संख्या है, यह कुछ ^ ^ n पर समाप्त नहीं होती है । लेकिन, अगर हम कुछ x ^ n पर रुकते हैं तब भी हमारे पास कुछ सटीक करने के लिए एक परिणाम होगा।

इसलिए, यदि हमारे पास:

y = 1/sqrt(x)

इस विशेष मामले में उन्होंने दूसरे बहुपद सदस्यों को दूसरे से पीछे छोड़ने का फैसला किया, शायद गणना गति के कारण:

y = a0 + a1*x + [...discarded...]

और कार्य अब y के लिए a0 और a1 की गणना करने के लिए नीचे आया है ताकि सटीक मान से कम से कम अंतर हो। उन्होंने गणना की है कि सबसे उपयुक्त मूल्य हैं:

a0 = 0x5f375a86
a1 = -0.5

इसलिए जब आप इसे समीकरण में डालते हैं तो आपको मिलता है:

y = 0x5f375a86 - 0.5*x

जो कोड में दिखाई देने वाली रेखा के समान है:

i = 0x5f375a86 - (i >> 1);

संपादित करें: वास्तव में यहाँ y = 0x5f375a86 - 0.5*xसमान नहीं है i = 0x5f375a86 - (i >> 1);क्योंकि फ्लोटिंग शिफ्टिंग पूर्णांक के रूप में न केवल दो से विभाजित होती है, बल्कि एक्सपोनेंट को भी दो से विभाजित करती है और कुछ अन्य कलाकृतियों का कारण बनती है, लेकिन यह अभी भी कुछ गुणांक a0, a1, a2 की गणना करने के लिए नीचे आती है ...

इस बिंदु पर उन्हें पता चला है कि इस परिणाम की सटीकता उद्देश्य के लिए पर्याप्त नहीं है। इसलिए उन्होंने अतिरिक्त सटीकता को बेहतर बनाने के लिए न्यूटन के पुनरावृत्ति का केवल एक कदम उठाया:

x = x * (1.5f - xhalf * x * x)

वे एक लूप में कुछ और पुनरावृत्तियों को कर सकते थे, प्रत्येक एक परिणाम में सुधार, जब तक आवश्यक सटीकता पूरी नहीं हो जाती। यह वास्तव में सीपीयू / एफपीयू में कैसे काम करता है! लेकिन ऐसा लगता है कि केवल एक पुनरावृत्ति ही पर्याप्त थी, जो गति के लिए एक आशीर्वाद भी थी। सीपीयू / एफपीयू फ्लोटिंग पॉइंट नंबर के लिए सटीकता तक पहुंचने के लिए कई पुनरावृत्तियों को करता है जिसमें परिणाम संग्रहीत होता है और इसमें अधिक सामान्य एल्गोरिदम होता है जो सभी मामलों के लिए काम करता है।


तो संक्षेप में, उन्होंने क्या किया:

CPU / FPU के समान एल्गोरिथ्म का उपयोग (लगभग), 1 / sqrt (x) के विशेष मामले के लिए प्रारंभिक स्थितियों में सुधार का फायदा उठाते हैं और सटीक CPU / FPU के लिए सभी तरह की गणना नहीं करते हैं, लेकिन पहले बंद कर देंगे, इस प्रकार गणना गति में प्राप्त करना।


2
सूचक को लंबे समय तक कास्टिंग करना log_2 (फ्लोट) का एक अनुमान है। इसे वापस कास्टिंग 2 ^ लंबे समय का एक अनुमान है। इसका मतलब है कि आप अनुपात को लगभग रैखिक बना सकते हैं।
wizzwizz4

22

कुछ समय पहले लिखे गए इस अच्छे लेख के अनुसार ...

कोड का जादू, भले ही आप इसका पालन न कर सकें, i = 0x5f3759df - (i >> i); लाइन। सरलीकृत, न्यूटन-रफसन एक अनुमान है जो एक अनुमान के साथ शुरू होता है और इसे पुनरावृत्ति के साथ परिष्कृत करता है। 32-बिट x86 प्रोसेसर की प्रकृति का लाभ उठाते हुए, मैं, एक पूर्णांक, शुरू में उस पूर्णांक संख्या के मूल्य पर सेट होता है, जिसका आप पूर्णांक कलाकारों का उपयोग करके उलटा वर्ग लेना चाहते हैं। मैं तब 0x5f3759df पर सेट होता हूं, माइनस खुद ही एक बिट को दाईं ओर स्थानांतरित कर देता है। सही बदलाव मैं कम से कम महत्वपूर्ण बिट ड्रॉप करता है, अनिवार्य रूप से इसे रोक रहा है।

यह वास्तव में अच्छा पढ़ा है। यह केवल इसका एक छोटा सा टुकड़ा है।


19

मैं यह देखने के लिए उत्सुक था कि निरंतर एक फ्लोट के रूप में क्या था इसलिए मैंने बस इस कोड को लिखा और पूर्णांक को पॉप आउट किया।

    long i = 0x5F3759DF;
    float* fp = (float*)&i;
    printf("(2^127)^(1/2) = %f\n", *fp);
    //Output
    //(2^127)^(1/2) = 13211836172961054720.000000

ऐसा लगता है कि स्थिरांक "2 के वर्गमूल के लिए एक पूर्णांक सन्निकटन है ^ अपने फ़्लोटिंग-पॉइंट प्रतिनिधित्व के हेक्साडेसिमल रूप से बेहतर 127 ज्ञात, 0x5f3759df" https://mrob.com/pub/math/noes-18.html

उसी साइट पर यह पूरी बात समझाता है। https://mrob.com/pub/math/numbers-16.html#le009_16


6
यह अधिक ध्यान देने योग्य है। यह सब समझ में आने के बाद समझ में आता है कि यह सिर्फ 2 ^ 127 का वर्गमूल है ...
u8y7541
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.