2012-01-15 20 views
393

मैं बी (एक = 2 ​​और बी = 50 कहें) की गणना के लिए एक कुशल दृष्टिकोण की तलाश में था। चीजों को शुरू करने के लिए, मैंने Math.Pow() फ़ंक्शन के कार्यान्वयन पर एक नज़र डालने का निर्णय लिया। लेकिन .NET Reflector में, सभी मैंने पाया यह था:Math.Pow() .NET Framework में कार्यान्वित कैसे किया जाता है?

[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical] 
public static extern double Pow(double x, double y); 

क्या संसाधनों के कुछ जिसमें मैं क्या हो रहा है के रूप में देख सकते हैं कर रहे हैं अंदर जब मैं Math.Pow() समारोह कहते हैं?

+15

बस एक एफवाईआई के रूप में, यदि आप 'बाहरी' मोडिफ़ायर के साथ पूरे 'आंतरिक कॉल' के बारे में उलझन में हैं (जैसे वे _seem_ विरोधाभासी होने के लिए), तो कृपया [प्रश्न (और परिणामी उत्तर)] देखें (http://stackoverflow.com/questions/1211462/c-sharp-internal-static-extern-with-internalcall-attribute-internal-or-externa) कि मैंने इस बारे में एक ही चीज़ पोस्ट की है। – CraigTP

+6

'x^x' ऑपरेशन के लिए यदि' x' पूर्णांक है तो परिणाम एक शिफ्ट ऑपरेशन है। तो हो सकता है कि आप '2' के मंटिसा और' x' के एक्सपोनेंट का उपयोग करके परिणाम बना सकें। – ja72

+0

@ सूरजजैन आपकी टिप्पणी वास्तव में एक प्रश्न है जिसे आपको अलग से पोस्ट करने की आवश्यकता है। – ja72

उत्तर

811

MethodImplOptions.InternalCall

इसका मतलब है कि विधि वास्तव में, CLR में कार्यान्वित किया जाता सी में लिखे ++। इन-इन-टाइम कंपाइलर आंतरिक रूप से कार्यान्वित तरीकों के साथ एक तालिका का उपभोग करता है और कॉल को सी ++ फ़ंक्शन पर सीधे संकलित करता है।

कोड को देखने के लिए सीएलआर के लिए स्रोत कोड की आवश्यकता है। आप इसे SSCLI20 distribution से प्राप्त कर सकते हैं। यह .NET 2.0 टाइम फ्रेम के आसपास लिखा गया था, मुझे निम्न स्तर के कार्यान्वयन मिले हैं, जैसे Math.Pow() सीएलआर के बाद के संस्करणों के लिए अभी भी काफी हद तक सटीक है।

लुकअप तालिका clr/src/vm/ecall.cpp में स्थित है। अनुभाग कि Math.Pow() के लिए प्रासंगिक है इस तरह दिखता है:

FCFuncStart(gMathFuncs) 
    FCIntrinsic("Sin", COMDouble::Sin, CORINFO_INTRINSIC_Sin) 
    FCIntrinsic("Cos", COMDouble::Cos, CORINFO_INTRINSIC_Cos) 
    FCIntrinsic("Sqrt", COMDouble::Sqrt, CORINFO_INTRINSIC_Sqrt) 
    FCIntrinsic("Round", COMDouble::Round, CORINFO_INTRINSIC_Round) 
    FCIntrinsicSig("Abs", &gsig_SM_Flt_RetFlt, COMDouble::AbsFlt, CORINFO_INTRINSIC_Abs) 
    FCIntrinsicSig("Abs", &gsig_SM_Dbl_RetDbl, COMDouble::AbsDbl, CORINFO_INTRINSIC_Abs) 
    FCFuncElement("Exp", COMDouble::Exp) 
    FCFuncElement("Pow", COMDouble::Pow) 
    // etc.. 
FCFuncEnd() 

"COMDouble" की खोज की पर ले जाता है clr/src/classlibnative/नाव/comfloat.cpp। मैं आपको कोड छोड़ दूंगा, बस अपने लिए एक नज़र डालें। यह मूल रूप से कोने के मामलों के लिए जांच करता है, फिर सीआरटी के pow() के संस्करण को कॉल करता है।

एकमात्र अन्य कार्यान्वयन विवरण जो दिलचस्प है तालिका में FCIntrinsic मैक्रो है। यह एक संकेत है कि जिटर एक आंतरिक के रूप में कार्य को कार्यान्वित कर सकता है। दूसरे शब्दों में, फ़्लोटिंग पॉइंट मशीन कोड निर्देश के साथ फ़ंक्शन कॉल को प्रतिस्थापित करें। जो Pow() के मामले में नहीं है, इसके लिए कोई एफपीयू निर्देश नहीं है। लेकिन निश्चित रूप से अन्य सरल संचालन के लिए। उल्लेखनीय है कि यह C++ में एक ही कोड की तुलना में सी # में फ्लोटिंग पॉइंट गणित को काफी तेज़ कर सकता है, कारण के लिए this answer देखें।

वैसे, यदि आपके पास Visual Studio vc/crt/src निर्देशिका का पूर्ण संस्करण है तो सीआरटी के लिए स्रोत कोड भी उपलब्ध है। आप दीवार को pow() पर हिट करेंगे हालांकि, माइक्रोसॉफ्ट ने इंटेल से उस कोड को खरीदा था। इंटेल इंजीनियरों की तुलना में बेहतर काम करना असंभव है। हालांकि मेरी उच्च विद्यालय किताब की पहचान दोगुनी गति से था जब मैं इसे करने की कोशिश:

public static double FasterPow(double x, double y) { 
    return Math.Exp(y * Math.Log(x)); 
} 

लेकिन एक सच्चे विकल्प है क्योंकि यह 3 से त्रुटि जम जाता है चल बिन्दु आपरेशनों और पागल डोमेन समस्याओं से निपटने के नहीं है कि पाउ () है। 0^0 और इंफिनिटी की तरह किसी भी शक्ति में उठाया गया।

+403

ग्रेट उत्तर, स्टैक ओवरफ्लो को इस तरह की चीज़ों की अधिक आवश्यकता है, 'आप उसे क्यों जानना चाहेंगे?' यह सब अक्सर होता है। –

+1

टॉम डब्ल्यू के साथ सहमत हैं शानदार जवाब। मैंने जो अपेक्षा की थी उससे ज्यादा सीखा। दिलचस्प बात यह है कि मैं आपकी पोस्ट को "उत्तर" के रूप में चिह्नित करके 1000 अंक तक पहुंच गया। एक बार फिर से धन्यवाद। –

+0

@ हंस मुझे यकीन नहीं है कि मैं अंतिम अनुच्छेद का पालन कर सकता हूं: यदि मैं 'crt/src/math.h' में देखता हूं तो मुझे वास्तव में' _Pow_int' (~ मेरी बनाम 2010 इंस्टॉल पर ~ लाइन 4 9 3) मिलती है जो कम से कम ऐसा लगता है मुझे सभी पाउ कॉल के लिए उपयोग किया जाता है। और यह स्पष्ट शिफ्ट की तरह लगता है और कार्यान्वयन गुणा करता है। क्या मुझसे कोई चूक हो रही है? (मुझे लगता है कि एक अनुकूलित इंटेल कार्यान्वयन कुछ प्रकार के एसएसई जादूगर का उपयोग करेगा? ऐसा लगता है कि हम इसे थोड़ा सा समांतर कर सकते हैं, लेकिन यह सुनिश्चित नहीं है कि यह एक सुधार होगा) – Voo

62

यदि freely available C version of pow कोई संकेत है, तो यह किसी भी चीज की तरह दिखता नहीं है। .NET संस्करण को खोजने में आपको बहुत मदद नहीं होगी, क्योंकि आप जिस समस्या को हल कर रहे हैं (यानी पूर्णांक वाले व्यक्ति) परिमाणों के आदेश सरल हैं, और सी # कोड with the exponentiation by squaring algorithm की कुछ पंक्तियों में हल किया जा सकता है।

+0

पर डाउनवोट प्रश्न पूछा जा सके, आपके उत्तर के लिए धन्यवाद। पहले लिंक ने मुझे आश्चर्यचकित कर दिया क्योंकि मुझे Pow() फ़ंक्शन के ऐसे बड़े तकनीकी कार्यान्वयन की उम्मीद नहीं थी। हालांकि हंस पासेंट उत्तर पुष्टि करता है कि यह नेट दुनिया में भी वही है। मुझे लगता है कि मैं स्क्वायरिंग एल्गोरिदम लिंक में सूचीबद्ध कुछ तकनीक का उपयोग करके समस्या को हल कर सकता हूं। एक बार फिर धन्यवाद। –

+2

मुझे विश्वास नहीं है कि यह कोड कुशल है। 30 स्थानीय चर केवल सभी रजिस्टरों को टक्कर देना चाहिए। मुझे लगता है कि यह एआरएम संस्करण है, लेकिन विधि में x86 30 स्थानीय चर पर भयानक है। –

96

Hans Passant's answer बहुत अच्छा है, लेकिन मैं यह जोड़ना चाहता हूं कि b एक पूर्णांक है, तो a^b बाइनरी अपघटन के साथ बहुत कुशलता से गणना की जा सकती है।

public static int iexp(int a, uint b) { 
    int y = 1; 

    while(true) { 
     if ((b & 1) != 0) y = a*y; 
     b = b >> 1; 
     if (b == 0) return y; 
     a *= a; 
    }  
} 

वह लिखते हैं कि इस आपरेशन इष्टतम है (करता गणित या तार्किक संचालन की न्यूनतम संख्या) सभी ख के लिए < 15. इसके अलावा वहाँ है करने के लिए कोई ज्ञात समाधान: यहाँ हेनरी वॉरेन हैकर के डिलाईट से एक संशोधित संस्करण है व्यापक खोज के अलावा किसी भी बी के लिए a^b की गणना करने के लिए कारकों का इष्टतम अनुक्रम खोजने की सामान्य समस्या। यह एक एनपी-हार्ड समस्या है। तो मूल रूप से इसका मतलब है कि बाइनरी अपघटन उतना ही अच्छा है जितना इसे मिलता है।

+11

यह एल्गोरिदम ([वर्ग और गुणा] (http://en.wikipedia.org/wiki/Exponentiation_by_squaring)) भी लागू होता है यदि 'ए 'एक फ्लोटिंग पॉइंट नंबर है। – CodesInChaos

+12

प्रैक्टिस में मूल वर्ग-और-गुणा से काफी बेहतर करना संभव है। उदाहरण के लिए छोटे एक्सपोनेंट्स के लिए लुकअप टेबल तैयार करना ताकि आप कई बार स्क्वायर कर सकें और केवल तभी गुणा कर सकें, या फिक्स्ड एक्सपोनेंट्स के लिए अनुकूलित स्क्वायर-एडिशन चेन का निर्माण कर सकें। इस तरह की समस्या महत्वपूर्ण क्रिप्टोग्राफिक एल्गोरिदम के अभिन्न अंग है, इसलिए इसे अनुकूलित करने पर काफी काम किया गया है। एनपी कठोरता केवल * सबसे खराब केस एसिम्प्टोटिक्स * के बारे में है, हम अक्सर अभ्यास में उत्पन्न होने वाली समस्या के उदाहरणों के लिए इष्टतम या निकटतम समाधान प्रदान कर सकते हैं। – CodesInChaos

संबंधित मुद्दे