किसी फ़ंक्शन का इनलाइन संस्करण नॉन-इनलाइन संस्करण की तुलना में भिन्न मान देता है


85

एक ही फ़ंक्शन के दो संस्करण, केवल एक इनलाइन में भिन्न हो सकते हैं और दूसरे में अलग-अलग मान नहीं हैं? यहाँ कुछ कोड है जो मैंने आज लिखा है और मुझे यकीन नहीं है कि यह कैसे काम करता है।

#include <cmath>
#include <iostream>

bool is_cube(double r)
{
    return floor(cbrt(r)) == cbrt(r);
}

bool inline is_cube_inline(double r)
{
    return floor(cbrt(r)) == cbrt(r);
}

int main()
{
    std::cout << (floor(cbrt(27.0)) == cbrt(27.0)) << std::endl;
    std::cout << (is_cube(27.0)) << std::endl;
    std::cout << (is_cube_inline(27.0)) << std::endl;
}

मैं सभी आउटपुट के बराबर होने की उम्मीद करूंगा 1, लेकिन यह वास्तव में इसे आउटपुट करता है (जी ++ 8.3.1, कोई झंडे नहीं):

1
0
1

के बजाय

1
1
1

संपादित करें: clang ++ 7.0.0 यह आउटपुट करता है:

0
0
0

और g ++

1
1
1

3
क्या आप कृपया संकलक, संकलक विकल्प आप और कौन सी मशीन का उपयोग कर रहे हैं प्रदान कर सकते हैं? विंडोज पर जीसीसी 7.1 पर मेरे लिए ठीक काम करता है।
डायोडाकस

31
==हमेशा फ्लोटिंग पॉइंट वैल्यू के साथ थोड़ा अप्रत्याशित नहीं होता है?
500 - आंतरिक सर्वर त्रुटि


2
क्या आपने -Ofastविकल्प निर्धारित किया था , जो इस तरह के अनुकूलन की अनुमति देता है?
cmdLP

4
कंपाइलर cbrt(27.0)उस मान के लिए वापस लौटता है 0x0000000000000840जबकि मानक पुस्तकालय लौटता है 0x0100000000000840। कॉमा के बाद 16 वीं संख्या में युगल भिन्न होते हैं। मेरे सिस्टम: archlinux4.20 64 gcc8.2.1 glibc2.28 साथ की जाँच की इस । आश्चर्य है अगर gcc या glibc सही है।
कामिलुक

जवाबों:


73

व्याख्या

कुछ संकलक (विशेषकर जीसीसी) संकलन समय पर अभिव्यक्तियों का मूल्यांकन करते समय उच्च परिशुद्धता का उपयोग करते हैं। यदि कोई अभिव्यक्ति केवल निरंतर इनपुट और शाब्दिक पर निर्भर करती है, तो इसका मूल्यांकन संकलन समय पर किया जा सकता है, भले ही अभिव्यक्ति को एक कॉन्स्ट्रेक्स चर में न सौंपा गया हो। ऐसा होता है या नहीं, इस पर निर्भर करता है:

  • अभिव्यक्ति की जटिलता
  • संकलन समय का मूल्यांकन करने का प्रयास करते समय थ्रेशोल्ड कंपाइलर कटऑफ के रूप में उपयोग करता है
  • विशेष मामलों में उपयोग की जाने वाली अन्य heuristics (जैसे कि जब क्लॉग एलॉप्स को हटाता है)

यदि एक अभिव्यक्ति स्पष्ट रूप से प्रदान की जाती है, जैसा कि पहले मामले में, इसकी कम जटिलता है और संकलक का संकलन समय पर इसका मूल्यांकन करने की संभावना है।

इसी तरह, यदि किसी फ़ंक्शन को इनलाइन चिह्नित किया जाता है, तो संकलक को संकलन समय पर इसका मूल्यांकन करने की अधिक संभावना होती है क्योंकि इनलाइन फ़ंक्शन उस सीमा को बढ़ाते हैं जिस पर मूल्यांकन हो सकता है।

उच्च-अनुकूलन स्तर भी इस सीमा को बढ़ाते हैं, जैसे -Ofast उदाहरण में, जहाँ उच्च परिशुद्धता संकलन-समय मूल्यांकन के कारण सभी अभिव्यक्तियाँ gcc पर सत्य का मूल्यांकन करते हैं।

हम कंपाइलर एक्सप्लोरर पर यहां इस व्यवहार का निरीक्षण कर सकते हैं। -O1 के साथ संकलित होने पर, केवल फ़ंक्शन इनलाइन का मूल्यांकन संकलन-समय पर किया जाता है, लेकिन -O3 में दोनों कार्यों का संकलन-समय पर मूल्यांकन किया जाता है।

नायब: संकलक-खोजकर्ता उदाहरणों में, मैं printfआईओस्ट्रीम के बजाय उपयोग करता हूं क्योंकि यह मुख्य फ़ंक्शन की जटिलता को कम करता है, जिससे प्रभाव अधिक दिखाई देता है।

प्रदर्शन जो inlineरनटाइम मूल्यांकन को प्रभावित नहीं करता है

हम यह सुनिश्चित कर सकते हैं कि मानक इनपुट से मूल्य प्राप्त करके किसी भी अभिव्यक्ति का संकलन समय पर मूल्यांकन नहीं किया जाता है, और जब हम ऐसा करते हैं, तो सभी 3 अभिव्यक्तियाँ यहाँ दिखाए गए अनुसार गलत होती हैं: https://ideone.com/QZbv6X

#include <cmath>
#include <iostream>

bool is_cube(double r)
{
    return floor(cbrt(r)) == cbrt(r);
}
 
bool inline is_cube_inline(double r)
{
    return floor(cbrt(r)) == cbrt(r);
}

int main()
{
    double value;
    std::cin >> value;
    std::cout << (floor(cbrt(value)) == cbrt(value)) << std::endl; // false
    std::cout << (is_cube(value)) << std::endl; // false
    std::cout << (is_cube_inline(value)) << std::endl; // false
}

इस उदाहरण के विपरीत , जहां हम समान संकलक सेटिंग्स का उपयोग करते हैं लेकिन संकलन-समय पर मूल्य प्रदान करते हैं, जिसके परिणामस्वरूप उच्च-सटीक संकलन-समय मूल्यांकन होता है।


22

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

फ्लोटिंग पॉइंट वैल्यू की तुलना करने का एक अच्छा तरीका लेख में उल्लिखित सापेक्ष सहिष्णुता परीक्षण है: फ्लोटिंग-पॉइंट टॉलरेंस पर दोबारा गौर किया गया

हम पहले Epsilon( सापेक्ष सहनशीलता ) मूल्य की गणना करते हैं जो इस मामले में होगा:

double Epsilon = std::max(std::cbrt(r), std::floor(std::cbrt(r))) * std::numeric_limits<double>::epsilon();

और फिर इस तरीके से इनलाइन और गैर-इनलाइन दोनों कार्यों में इसका उपयोग करें:

return (std::fabs(std::floor(std::cbrt(r)) - std::cbrt(r)) < Epsilon);

अब कार्य हैं:

bool is_cube(double r)
{
    double Epsilon = std::max(std::cbrt(r), std::floor(std::cbrt(r))) * std::numeric_limits<double>::epsilon();    
    return (std::fabs(std::floor(std::cbrt(r)) - std::cbrt(r)) < Epsilon);
}

bool inline is_cube_inline(double r)
{
    double Epsilon = std::max(std::cbrt(r), std::floor(std::cbrt(r))) * std::numeric_limits<double>::epsilon();
    return (std::fabs(std::round(std::cbrt(r)) - std::cbrt(r)) < Epsilon);
}

अब आउटपुट [1 1 1]विभिन्न कंपाइलरों के साथ और अलग-अलग अनुकूलन स्तरों पर अपेक्षित ( ) होगा ।

लाइव डेमो


max()कॉल का उद्देश्य क्या है ? परिभाषा के अनुसार, floor(x)इससे कम या बराबर है x, इसलिए max(x, floor(x))हमेशा बराबर रहेगा x
केन थॉमासेस

@KenThomases: इस विशेष मामले में, जहां एक तर्क maxसिर्फ floorदूसरे का है, इसकी आवश्यकता नहीं है। लेकिन मैंने एक सामान्य मामले पर विचार किया जहां तर्क वे maxमूल्य या भाव हो सकते हैं जो एक दूसरे से स्वतंत्र होते हैं।
पीडब्ल्यू

operator==(double, double)बिल्कुल ऐसा नहीं करना चाहिए , एक स्केल एप्सिलॉन से छोटे होने के लिए अंतर की जांच करें? एसओ पर फ़्लोटिंग पॉइंट से संबंधित 90% प्रश्न तब मौजूद नहीं थे।
पीटर - मोनिका

मुझे लगता है कि यह बेहतर है अगर उपयोगकर्ता Epsilonको उनकी विशेष आवश्यकता के आधार पर मूल्य निर्दिष्ट करने के लिए मिलता है ।
पीडब्ल्यू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.