2011-09-22 13 views
98

नीचे दिया गया कोड विजुअल स्टूडियो 2008 पर और बिना अनुकूलन के काम करता है। लेकिन यह केवल अनुकूलन (ओ 0) के बिना जी ++ पर काम करता है।ऑप्टिमाइज़ेशन सक्षम के साथ विभिन्न फ़्लोटिंग पॉइंट परिणाम - कंपाइलर बग?

#include <cstdlib> 
#include <iostream> 
#include <cmath> 

double round(double v, double digit) 
{ 
    double pow = std::pow(10.0, digit); 
    double t = v * pow; 
    //std::cout << "t:" << t << std::endl; 
    double r = std::floor(t + 0.5); 
    //std::cout << "r:" << r << std::endl; 
    return r/pow; 
} 

int main(int argc, char *argv[]) 
{ 
    std::cout << round(4.45, 1) << std::endl; 
    std::cout << round(4.55, 1) << std::endl; 
} 

उत्पादन किया जाना चाहिए:

4.5 
4.6 

लेकिन जी अनुकूलन (O1-O3) के साथ ++ होगा उत्पादन:

4.5 
4.5 

अगर मैं टी से पहले volatile कीवर्ड जोड़ने, यह काम करता है, तो क्या किसी प्रकार का अनुकूलन बग हो सकता है? जी पर

टेस्ट ++ 4.1.2, और 4.4.4।

यहाँ ideone पर परिणाम है: http://ideone.com/Rz937

और विकल्प मैं जी पर परीक्षण ++ सरल है:

g++ -O2 round.cpp 

अधिक दिलचस्प परिणाम, यहां तक ​​कि मैं विजुअल स्टूडियो 2008 पर /fp:fast विकल्प को चालू, परिणाम अभी भी सही है।

इसके अलावा प्रश्न:

मैं सोच रहा था, मैं हमेशा -ffloat-store विकल्प को चालू करना चाहिए?

क्योंकि जी ++ संस्करण मैं परीक्षण किया CentOS/Red Hat Linux 5 और CentOS/redhat 6 के साथ भेज दिया है।

मैं इन प्लेटफार्मों के तहत मेरे कार्यक्रमों में से कई संकलित, और मैं चिंतित यह मेरे कार्यक्रमों के अंदर अप्रत्याशित बग का कारण होगा हूँ। मेरे सभी सी ++ कोड की जांच करना और पुस्तकालयों का उपयोग करना थोड़ा मुश्किल लगता है कि उन्हें ऐसी समस्याएं हैं या नहीं। कोई उपाय? क्यों भी /fp:fast चालू है, विजुअल स्टूडियो 2008 अभी भी काम करता है

किसी में रुचि है? ऐसा लगता है कि विजुअल   स्टूडियो   2008 जी ++ की तुलना में इस समस्या पर अधिक विश्वसनीय है?

+49

सभी नए SO उपयोगकर्ताओं के लिए: यह है कि आप एक प्रश्न कैसे पूछते हैं। +1 – tenfour

+1

FWIW, मुझे MinGW का उपयोग कर g ++ 4.5.0 के साथ सही आउटपुट मिल रहा है। –

+0

मुझे सभी मामलों के लिए 4.5 4.6 मिलते हैं। आपका जी ++ संस्करण क्या है? मेरे पास g ++ (डेबियन 4.3.2-1.1) 4.3.2 –

उत्तर

79

इंटेल x86 प्रोसेसर का उपयोग 80-बिट आंतरिक रूप से बढ़ाया परिशुद्धता, जबकि double सामान्य रूप से 64-बिट चौड़ा है।विभिन्न अनुकूलन स्तर प्रभावित करते हैं कि CPU से फ़्लोटिंग पॉइंट मान कितनी बार स्मृति में सहेजे जाते हैं और इस प्रकार 80-बिट परिशुद्धता से 64-बिट परिशुद्धता तक गोल होते हैं।

विभिन्न ऑप्टिमाइज़ेशन स्तरों के साथ समान फ़्लोटिंग पॉइंट परिणाम प्राप्त करने के लिए -ffloat-store जीसीसी विकल्प का उपयोग करें।

वैकल्पिक रूप से, long double प्रकार का उपयोग करें, जो आमतौर पर 80-बिट से 64-बिट परिशुद्धता से घूमने से बचने के लिए जीसीसी पर 80-बिट चौड़ा होता है।

man gcc यह कहता है कि सब:

-ffloat-store 
     Do not store floating point variables in registers, and inhibit 
     other options that might change whether a floating point value is 
     taken from a register or memory. 

     This option prevents undesirable excess precision on machines such 
     as the 68000 where the floating registers (of the 68881) keep more 
     precision than a "double" is supposed to have. Similarly for the 
     x86 architecture. For most programs, the excess precision does 
     only good, but a few programs rely on the precise definition of 
     IEEE floating point. Use -ffloat-store for such programs, after 
     modifying them to store all pertinent intermediate computations 
     into variables. 
+20

मुझे लगता है कि यह जवाब है। निरंतर 4.55 4.54999 99 99 99 999 में परिवर्तित हो जाता है जो 64 बिट्स में सबसे नज़दीकी बाइनरी प्रतिनिधित्व है; 10 से गुणा करें और फिर 64 बिट्स तक गोल करें और आपको 45.5 मिलें। यदि आप इसे 80-बिट रजिस्टर में रखते हुए राउंडिंग चरण छोड़ते हैं तो आप 45.4 99 99 99 99 99 99 के साथ समाप्त होते हैं। –

+0

धन्यवाद, मुझे यह विकल्प भी नहीं पता। लेकिन मैं सोच रहा था, क्या मुझे हमेशा -फ्लैट-स्टोर विकल्प चालू करना चाहिए? चूंकि मैंने परीक्षण किए गए जी ++ संस्करण को सेंटोस/रेडहाट 5 और सेंटोस/रेडहाट 6 के साथ भेज दिया है। मैंने इन प्लेटफार्मों के तहत अपने कई कार्यक्रमों को संकलित किया है, मुझे चिंता है कि इससे मेरे कार्यक्रमों के अंदर अप्रत्याशित बग का कारण बन जाएगा। – Bear

+0

@ मार्क: लेकिन अगर आप डीबग स्टेटमेंट को अनमोल करते हैं तो कैसे समझाएं: std :: cout << "t:" << t << std :: endl; आउटपुट सही होगा ??? – Bear

6

विभिन्न compilers अलग अनुकूलन सेटिंग है। उनमें से कुछ तेज़ ऑप्टिमाइज़ेशन सेटिंग्स IEEE 754 के अनुसार सख्त फ़्लोटिंग-पॉइंट नियमों को बनाए नहीं रखती हैं। विजुअल स्टूडियो एक विशिष्ट सेटिंग, /fp:strict, /fp:precise, /fp:fast, जहां /fp:fast क्या किया जा सकता है पर मानक का उल्लंघन करता है। आपको लगता है कि यह ध्वज ऐसी सेटिंग्स में अनुकूलन को नियंत्रित करता है। आपको जीसीसी में एक समान सेटिंग भी मिल सकती है जो व्यवहार को बदलती है।

यदि ऐसा है तो कंपेलरों के बीच एकमात्र चीज यह है कि जीसीसी उच्च अनुकूलन पर डिफ़ॉल्ट रूप से सबसे तेज़ फ्लोटिंग पॉइंट व्यवहार की तलाश करेगा, जबकि विजुअल स्टूडियो उच्च अनुकूलन स्तरों के साथ फ़्लोटिंग पॉइंट व्यवहार को नहीं बदलता है। इस प्रकार यह एक वास्तविक बग नहीं होना चाहिए, लेकिन एक विकल्प का इच्छित व्यवहार जिसे आप नहीं जानते थे, आप चालू कर रहे थे।

+4

जीसीसी के लिए '-फैस्ट-गणित' स्विच है, और यह उद्धरण के बाद से किसी भी 'ओओ' ऑप्टिमाइज़ेशन स्तर से चालू नहीं है: "इसके परिणामस्वरूप प्रोग्रामों के लिए गलत आउटपुट हो सकता है जो आईईईई के सटीक कार्यान्वयन पर निर्भर करता है या गणित कार्यों के लिए आईएसओ नियम/विनिर्देश। " – Mat

+0

@ मैट: मैंने '-फस्ट-गणित' और कुछ अन्य चीजों को अपने 'g ++ 4.4.3' पर आजमाया है और मैं अभी भी समस्या को पुन: उत्पन्न करने में असमर्थ हूं। – NPE

+0

अच्छा: '-फस्ट-गणित' के साथ मुझे '0' से अधिक अनुकूलन स्तर के लिए दोनों मामलों में' 4.5' मिलता है। –

3

जो लोग बग पुन: पेश नहीं कर सकते करने के लिए: डिबग stmts बाहर टिप्पणी की uncomment नहीं करते, वे परिणाम प्रभावित करते हैं।

इसका तात्पर्य है कि समस्या डीबग स्टेटमेंट से संबंधित है। और ऐसा लगता है कि वहाँ एक पूर्णांकन उत्पादन बयानों के दौरान रजिस्टरों में मूल्यों लोड हो रहा है की वजह से त्रुटि है, जिसके कारण अन्य लोगों ने पाया है कि आप -ffloat-store

इसके अलावा सवाल के साथ इसे ठीक कर सकते है जैसे:

मैं सोच रहा था, ऐसा करना चाहिए मैं हमेशा -ffloat-store विकल्प चालू करता हूं?

क्षुद्र हो करने के लिए, एक कारण यह है कि कुछ प्रोग्रामर -ffloat-store चालू नहीं करते, अन्यथा विकल्प मौजूद नहीं हैं (इसी तरह, एक कारण यह है कि कुछ प्रोग्रामर -ffloat-store पर बारी कर वहाँ होना चाहिए) होना चाहिए । मैं इसे हमेशा चालू या हमेशा बंद करने की अनुशंसा नहीं करता। इसे चालू करने से कुछ ऑप्टिमाइज़ेशन रोकते हैं, लेकिन इसे बंद करने से आपके द्वारा प्राप्त किए जा रहे व्यवहार के लिए अनुमति मिलती है।

लेकिन, आम तौर पर, some mismatch बाइनरी फ्लोटिंग पॉइंट नंबरों (जैसे कंप्यूटर उपयोगों) और दशमलव फ़्लोटिंग पॉइंट नंबर (जो लोग परिचित हैं) के बीच some mismatch है, और यह मेलसमूह आपके प्राप्त होने के समान व्यवहार कर सकता है (स्पष्ट होना , जो व्यवहार आप प्राप्त कर रहे हैं वह इस मेल के कारण नहीं है, लेकिन समान व्यवहार हो सकता है)। बात यह है कि, फ्लोटिंग पॉइंट से निपटने के दौरान आपके पास पहले से ही कुछ अस्पष्टता है, इसलिए मैं यह नहीं कह सकता कि -ffloat-store इसे किसी भी बेहतर या किसी भी बदतर बनाता है।

इसके बजाय, आप जिस समस्या को हल करने की कोशिश कर रहे हैं उस समस्या के लिए आप other solutions पर देखना चाह सकते हैं (दुर्भाग्य से, कोएनिग वास्तविक कागज़ को इंगित नहीं करता है, और मुझे इसके लिए वास्तव में एक स्पष्ट "कैनोलिक" स्थान नहीं मिल रहा है , इसलिए मुझे आपको Google पर भेजना होगा)।


आप उत्पादन प्रयोजनों के लिए गोलाई नहीं कर रहे हैं, मैं शायद std::modf() (cmath में) और std::numeric_limits<double>::epsilon() (limits में) पर विचार करेंगे।मूल round() समारोह की सोच रही थी, मेरा मानना ​​है कि यह इस समारोह के लिए एक कॉल के साथ std::floor(d + .5) करने के लिए कॉल को बदलने के लिए क्लीनर होगा:

// this still has the same problems as the original rounding function 
int round_up(double d) 
{ 
    // return value will be coerced to int, and truncated as expected 
    // you can then assign the int to a double, if desired 
    return d + 0.5; 
} 

मुझे लगता है कि निम्नलिखित सुधार पता चलता है:

// this won't work for negative d ... 
// this may still round some numbers up when they should be rounded down 
int round_up(double d) 
{ 
    double floor; 
    d = std::modf(d, &floor); 
    return floor + (d + .5 + std::numeric_limits<double>::epsilon()); 
} 

एक साधारण नोट: std::numeric_limits<T>::epsilon() को "सबसे छोटा नंबर 1 में जोड़ा गया है जो एक संख्या बनाता है जो 1 के बराबर नहीं है।" आपको आमतौर पर एक रिश्तेदार ईपीएसलॉन (यानी, स्केल ईपीएसलॉन किसी भी तरह से इस तथ्य के लिए खाते की आवश्यकता होती है कि आप "1" के अलावा अन्य नंबरों के साथ काम कर रहे हैं)। d, .5 और std::numeric_limits<double>::epsilon() का योग 1 के करीब होना चाहिए, इसलिए समूह को जोड़ने का अर्थ है कि std::numeric_limits<double>::epsilon() हम जो कर रहे हैं उसके लिए सही आकार के बारे में होगा। यदि कुछ भी हो, std::numeric_limits<double>::epsilon() बहुत बड़ा होगा (जब सभी तीनों का योग एक से कम है) और हमें कुछ संख्याओं को गोल करने का कारण बन सकता है जब हमें नहीं करना चाहिए।


आजकल, आप std::nearbyint() विचार करना चाहिए।

+0

ए "रिश्तेदार ईपीएसलॉन" को 1 उल (1 स्थान अंतिम स्थान पर) कहा जाता है। 'एक्स - इसके बाद (एक्स, इन्फिनिटी) 'x के लिए 1 ulp से संबंधित है (लेकिन इसका उपयोग न करें; मुझे यकीन है कि कोने के मामले हैं और मैंने अभी इसे बनाया है)। 'Epsilon() 'के लिए cppreference उदाहरण [यूएलपी-आधारित सापेक्ष त्रुटि प्राप्त करने के लिए इसे स्केल करने का एक उदाहरण है] (http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon)। –

+2

बीटीडब्लू, 2016 का जवाब '-फ्लोट-स्टोर' है: x87 का उपयोग पहले स्थान पर न करें। एसएसई 2 गणित (64-बिट बाइनरी, या '-mfpmath = sse -msse2' क्रिस्टी पुरानी 32-बिट बाइनरी बनाने के लिए) का उपयोग करें, क्योंकि एसएसई/एसएसई 2 में अस्थायी नहीं हैं जिनमें कोई अतिरिक्त परिशुद्धता नहीं है। एक्सएमएम रजिस्टरों में 'डबल' और 'फ्लोट' वर्र्स वास्तव में आईईईई 64-बिट या 32-बिट प्रारूप में हैं। (X87 के विपरीत, जहां पंजीयक हमेशा 80-बिट होते हैं, और मेमोरी राउंड को 32 या 64 बिट तक संग्रहीत करते हैं।) –

10

आउटपुट होना चाहिए: 4.5 4.6 है यही कारण है कि उत्पादन करता है, तो आप अनंत सटीक था या हो सकता है, आप एक युक्ति है कि एक दशमलव आधारित बजाय द्विआधारी आधारित फ्लोटिंग बिन्दु निरूपण इस्तेमाल किया साथ काम कर रहे थे, तो। लेकिन, आप नहीं हैं। अधिकांश कंप्यूटर बाइनरी आईईईई फ्लोटिंग पॉइंट मानक का उपयोग करते हैं।

मैक्सिम Yegorushkin जैसा कि पहले ही, उसके जवाब में बताया गया है हिस्सा समस्या का कि आंतरिक रूप से आपके कंप्यूटर एक 80 बिट चल बिन्दु प्रतिनिधित्व का उपयोग कर रहा है। हालांकि, यह समस्या का सिर्फ एक हिस्सा है। समस्या का आधार यह है कि किसी भी रूप में n.nn5 में कोई बाइनरी फ़्लोटिंग प्रतिनिधित्व नहीं है। उन कोने के मामले हमेशा निष्क्रिय संख्याएं हैं।

यदि आप वास्तव में चाहते हैं कि आपका राउंडिंग इन कोने के मामलों को विश्वसनीय रूप से पार करने में सक्षम हो, तो आपको एक गोल करने वाले एल्गोरिदम की आवश्यकता है जो इस तथ्य को संबोधित करता है कि n.n5, n.nn5, या n.nnn5, आदि (लेकिन नहीं। 5) हमेशा अचूक है। कोने केस ढूंढें जो यह निर्धारित करता है कि कुछ इनपुट मान ऊपर या नीचे गोल करते हैं और इस कोने केस की तुलना के आधार पर गोलाकार-अप या गोल-डाउन मान वापस करते हैं। और आपको यह ध्यान रखना होगा कि एक अनुकूलन कंपाइलर उस विस्तारित परिशुद्धता रजिस्टर में उस कोने केस को नहीं रखेगा।

ऐसे एल्गोरिदम के लिए How does Excel successfully Rounds Floating numbers even though they are imprecise? देखें।

या आप इस तथ्य के साथ रह सकते हैं कि कोने के मामले कभी-कभी गलती से दौर में होंगे।

0

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

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