यदि मैं किसी अन्य चर के लिए एक फ्लोट की प्रतिलिपि बनाता हूं, तो क्या वे समान होंगे?


167

मुझे पता है कि ==फ्लोटिंग-पॉइंट चर की समानता की जांच करने का उपयोग करना एक अच्छा तरीका नहीं है। लेकिन मैं सिर्फ यह जानना चाहता हूं कि निम्नलिखित कथनों के साथ:

float x = ...

float y = x;

assert(y == x)

चूंकि yनकल की जाती है x, क्या यह दावा सही होगा?


78
मुझे वास्तविक रूप से एक प्रदर्शन द्वारा असमानता साबित करने वाले किसी व्यक्ति को 50 का इनाम प्रदान करें। मैं एक्शन में 80 बनाम 64 बिट चीज देखना चाहता हूं। इसके अलावा उत्पन्न कोडर कोड के स्पष्टीकरण के लिए एक और 50 जो एक चर को एक रजिस्टर में होता है और दूसरा नहीं (या असमानता का कारण जो भी हो, मैं चाहता हूं कि यह निम्न स्तर पर समझाया जाए)।
थॉमस वेलर

1
@ThomasWeller इस बारे में जीसीसी बग: gcc.gnu.org/bugzilla/show_bug.cgi?id=323 ; हालाँकि, मैंने अभी-अभी इसे x86-64 सिस्टम पर रीप्रो करने की कोशिश की है और यह -फास्ट-मैथ के साथ भी नहीं है। मुझे संदेह है कि आपको 32-बिट सिस्टम पर एक पुराने जीसीसी की आवश्यकता है।
15:50 बजे pjc50

5
@ pjc50: वास्तव में आपको बग 323 को पुन: पेश करने के लिए 80-बिट सिस्टम की आवश्यकता है; यह 80x87 एफपीयू है जो समस्या का कारण बना। x86-64 SSE FPU का उपयोग करता है। अतिरिक्त बिट्स समस्या का कारण बनते हैं, क्योंकि वे 32 बिट्स फ्लोट के मान को गोल करते समय गोल होते हैं।
MSalters

4
यदि MSalters का सिद्धांत सही है (और मुझे संदेह है कि यह) है, तो आप 32-बिट ( -m32) के लिए संकलन करके या x87 FPU ( -mfpmath=387) का उपयोग करने के लिए GCC को निर्देश देकर या तो फिर से कर सकते हैं ।
कोड़ी ग्रे

4
"48 बिट" को "80 बिट" में बदलें, और फिर आप वहां "पौराणिक" विशेषण, @ हटा सकते हैं। आपकी टिप्पणी से ठीक पहले चर्चा की जा रही थी। X87 (x86 आर्किटेक्चर के लिए FPU) 80-बिट रजिस्टरों, एक "विस्तारित-सटीक" प्रारूप का उपयोग करता है।
कोड़ी ग्रे

जवाबों:


125

assert(NaN==NaN);किमीड्रेको द्वारा इंगित किए गए मामले के अलावा , आपके पास x87-गणित के साथ स्थितियां हो सकती हैं, जब 80bit फ़्लोट्स को मेमोरी में अस्थायी रूप से संग्रहीत किया जाता है और बाद में उन मानों की तुलना में जो अभी भी एक रजिस्टर के अंदर संग्रहीत हैं।

संभव न्यूनतम उदाहरण, जो gcc9.2 के साथ विफल होने पर संकलित किया जाता है -O2 -m32:

#include <cassert>

int main(int argc, char**){
    float x = 1.f/(argc+2);
    volatile float y = x;
    assert(x==y);
}

गॉडबोल्ट डेमो: https://godbolt.org/z/X-Xt4R

volatileशायद, छोड़ा जा सकता है अगर आप पर्याप्त रजिस्टर दबाव बनाने के लिए प्रबंधन करने के लिए है yसंग्रहीत और स्मृति से पुनः लोड (लेकिन संकलक पर्याप्त, तुलना न करने का विकल्प नहीं सभी एक साथ भ्रमित)।

देखें GCC FAQ संदर्भ:


2
यह अजीब लगता है कि अतिरिक्त बिट्स को floatमानक परिशुद्धता के साथ अतिरिक्त परिशुद्धता की तुलना में माना जाएगा ।
नेट

13
@Nat यह है अजीब; यह एक बग है
को ऑर्बिट

13
@ThomasWeller नहीं, यह एक उचित पुरस्कार है। हालांकि मैं इस बात का जवाब देना चाहूंगा कि यह गैर-आज्ञाकारी व्यवहार है
लाइटनेस दौड़ ऑर्बिट में

4
मैं इस उत्तर का विस्तार कर सकता हूं, यह बताता है कि विधानसभा कोड में वास्तव में क्या होता है, और यह वास्तव में मानक का उल्लंघन करता है - हालांकि मैं खुद को भाषा-वकील नहीं कहूंगा, इसलिए मैं गारंटी नहीं दे सकता कि कोई अस्पष्ट नहीं है क्लॉज जो स्पष्ट रूप से उस व्यवहार की अनुमति देता है। मुझे लगता है कि ओपी वास्तविक संकलक पर व्यावहारिक जटिलताओं में अधिक रुचि रखता था, पूरी तरह से बग-मुक्त नहीं, पूरी तरह से आज्ञाकारी संकलक (जो वास्तव में मौजूद नहीं है, मुझे लगता है)।
chtz

4
इसका उल्लेख -ffloat-storeकरने का तरीका प्रतीत होता है।
ऑरेंजडॉग

116

यह अगर सच नहीं हो जाएगा xहै NaN, के बाद से तुलना पर NaNकर रहे हैं हमेशा गलत (हाँ, यहाँ तक कि NaN == NaN)। अन्य सभी मामलों के लिए (सामान्य मूल्य, उप-सामान्य मूल्य, शिशु, शून्य) यह दावा सही होगा।

==फ़्लोट्स से बचने के लिए सलाह फ़्लोटिंग पॉइंट नंबरों के लिए गणना पर लागू होती है, जो अंकगणितीय अभिव्यक्तियों में उपयोग किए जाने पर वास्तव में कई परिणाम व्यक्त करने में असमर्थ हैं। असाइनमेंट कोई गणना नहीं है और ऐसा कोई कारण नहीं है कि असाइनमेंट मूल की तुलना में एक अलग मूल्य प्राप्त करेगा।


यदि मानक का पालन किया जाता है, तो विस्तारित-सटीक मूल्यांकन एक गैर-मुद्दा होना चाहिए। <cfloat>C से विरासत में मिला [5.2.4.2.2.8] ( जोर मेरा ):

असाइनमेंट और कास्ट को छोड़कर (जो सभी अतिरिक्त रेंज और परिशुद्धता को हटाते हैं) , फ्लोटिंग ऑपरेशंस के साथ संचालन के मूल्यों और सामान्य अंकगणितीय रूपांतरणों और फ्लोटिंग स्थिरांक के मूल्यों का मूल्यांकन एक प्रारूप में किया जाता है, जिसकी सीमा और परिशुद्धता आवश्यकता से अधिक हो सकती है। प्रकार।

हालाँकि, जैसा कि टिप्पणियों ने बताया है, कुछ संकलक, बिल्ड-ऑप्शंस और लक्ष्य के साथ कुछ मामले इस विरोधाभास को गलत बना सकते हैं।


10
क्या होगा अगर xपहली पंक्ति में एक रजिस्टर में गणना की जाती है, एक के लिए न्यूनतम से अधिक परिशुद्धता रखते हुए floaty = xस्मृति में हो सकता है, केवल रखने floatपरिशुद्धता। फिर समानता के लिए परीक्षण अलग-अलग प्राथमिकताओं के साथ रजिस्टर के खिलाफ मेमोरी के साथ किया जाएगा, और इस प्रकार कोई गारंटी नहीं है।
डेविड श्वार्ट्ज

5
x+pow(b,2)==x+pow(a,3)भिन्न हो सकते हैं auto one=x+pow(b,2); auto two=y+pow(a,3); one==twoक्योंकि एक दूसरे की तुलना में अधिक सटीकता का उपयोग करने की तुलना कर सकता है (यदि एक / दो में 64 बिट मान हैं RAM, जबकि इंटरमेडिस्ट के मान fish पर 80ish बिट्स हैं)। तो असाइनमेंट कभी-कभी कुछ कर सकता है।
यक्क - एडम नेवरुमोंट

22
@ येग ज़रूर! मेरा जवाब बस मानक का पालन करता है। यदि आप अपने कंपाइलर को गैर-कंफर्टेबल बताते हैं, तो विशेष रूप से फास्ट-मैथ को सक्षम करने पर सभी दांव बंद हो जाते हैं।
किमीड्रेको

11
@ मेरे जवाब में बोली देखें। RHS का मान LHS पर चर को सौंपा गया है। एलएचएस के परिणामी मूल्य के लिए आरएचएस के मूल्य से भिन्न होने का कोई कानूनी औचित्य नहीं है। मैं सराहना करता हूं कि इस संबंध में कई संकलकों में बग हैं। लेकिन क्या किसी चीज को किसी रजिस्टर में स्टोर किया जाता है, इससे कोई लेना देना नहीं है।
ऑर्बिट में लाइटनेस दौड़

6
@Voo: ISO C ++ में, किसी भी असाइनमेंट पर राउंड टू टाइप चौड़ाई होनी चाहिए। अधिकांश कंपाइलर जो x87 को लक्षित करते हैं, यह वास्तव में केवल तब होता है जब कंपाइलर स्पिल / रीलोड करने का निर्णय लेता है। आप इसे gcc -ffloat-storeसख्त अनुपालन के लिए बाध्य कर सकते हैं । लेकिन यह सवाल x=y; x==y; बीच में var के लिए कुछ भी किए बिना है। यदि yपहले से ही एक फ्लोट में फिट होने के लिए गोल किया गया है, तो डबल या लॉन्ग डबल और बैक में परिवर्तित करने से मान नहीं बदलेगा। ...
पीटर कॉर्डेस

34

हां, yनिश्चित रूप से मूल्य पर ले जाएगा x:

[expr.ass]/2: साधारण असाइनमेंट (=) में, बाएं ऑपरेंड द्वारा संदर्भित ऑब्जेक्ट को संशोधित किया जाता है ([defns.access]) इसके मूल्य को सही ऑपरेंड के परिणाम के साथ बदलकर।

असाइन किए जाने वाले अन्य मानों के लिए कोई शुल्क नहीं है।

(अन्य लोग पहले ही बता चुके हैं कि समतुल्यता तुलना ==फिर भी falseNaN मूल्यों के लिए मूल्यांकन करेगी ।)

फ़्लोटिंग-पॉइंट के साथ सामान्य मुद्दा ==यह है कि यह काफी आसान नहीं है कि आपको लगता है कि आप करते हैं। यहां, हम जानते हैं कि दो मूल्य, जो कुछ भी हैं, समान हैं।


7
@ThomasWeller इसके परिणामस्वरूप गैर-अनुपालन कार्यान्वयन में एक ज्ञात बग है। यह उल्लेख हालांकि अच्छा है!
को ऑर्बिट

सबसे पहले, मैंने सोचा था कि "मान" और "परिणाम" के बीच के अंतर को निर्धारित करने वाली भाषा विकृत होगी, लेकिन इस अंतर को C2.2, 7.1.6 की भाषा के बिना अंतर के होने की आवश्यकता नहीं है; सी 3.3, 7.1.6; C4.2, 7.1.6, या C5.3, 7.1.6 मसौदा के मानक आप का हवाला देते हैं।
एरिक टावर्स

@EricTowers क्या आप उन संदर्भों को स्पष्ट कर सकते हैं? मुझे पता नहीं है कि आप किस ओर इशारा कर रहे हैं
ऑर्बिट

@ LightnessRacesBY-SA3.0: सीC2.2 , C3.3 , C4.2 , और C5.3
एरिक टावर्स

@EricTowers हाँ, अभी भी आपका पीछा नहीं कर रहा है। आपका पहला लिंक परिशिष्ट C सूचकांक में जाता है (मुझे कुछ भी नहीं बताता है)। आपका अगला चार लिंक सभी के लिए जाना [expr]। यदि मैं लिंक को अनदेखा करता हूं और उद्धरणों पर ध्यान केंद्रित करता हूं, तो मैं इस भ्रम के साथ छोड़ दिया जाता हूं कि उदाहरण के लिए C.5.3 शब्द "मूल्य" या "परिणाम" शब्द के उपयोग को संबोधित नहीं करता है (हालांकि यह करता है) "परिणाम" को उसके सामान्य अंग्रेजी संदर्भ में एक बार उपयोग करें)। शायद आप अधिक स्पष्ट रूप से वर्णन कर सकते हैं जहां आपको लगता है कि मानक एक भेद करता है, और ऐसा होने के लिए एक ही स्पष्ट उद्धरण प्रदान करता है। धन्यवाद!
ऑर्बिट

3

हां, सभी मामलों में (NaN और x87 मुद्दों की अवहेलना), यह सच होगा।

यदि आप memcmpउन पर करते हैं तो आप NaN और sNaN की तुलना करते हुए समानता के लिए परीक्षण कर पाएंगे। इसके लिए वैरिएबल का पता लेने के लिए कंपाइलर की भी आवश्यकता होगी जो कि float80-बिट वाले के बजाय 32-बिट में वैल्यू को कम करेगा । यह x87 मुद्दों को समाप्त कर देगा। यहाँ दूसरा दावा यह दिखाने में विफल है कि ==यह सच है कि NaNs की तुलना नहीं करेगा:

#include <cmath>
#include <cassert>
#include <cstring>

int main(void)
{
    float x = std::nan("");
    float y = x;
    assert(!std::memcmp(&y, &x, sizeof(float)));
    assert(y == x);
    return 0;
}

ध्यान दें कि यदि NaN में एक अलग आंतरिक प्रतिनिधित्व (यानी अलग-अलग मंटिसा) है, तो memcmpयह सच की तुलना नहीं करेगा।


1

सामान्य मामलों में, यह सच का मूल्यांकन करेगा। (या मुखर कथन कुछ नहीं करेगा)

संपादित करें :

'सामान्य मामलों' से मेरा तात्पर्य अन्य उपयोगकर्ताओं द्वारा बताए अनुसार उपरोक्त परिदृश्य (जैसे NaN मान और 80x87 फ्लोटिंग पॉइंट यूनिट) को छोड़कर है।

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

(संदर्भ 8087 - https://home.deec.uc.pt/~jlobo/tc/artofasm/ch14/ch143.htm )

कुदोस @ नात्ज़ को एक अच्छा उदाहरण और @kmdreko के लिए NaNs का उल्लेख करने के लिए - पहले उनके बारे में पता नहीं था!


1
मुझे लगा कि मेमोरी से लोड होने के xदौरान फ्लोटिंग पॉइंट रजिस्टर में रहना पूरी तरह से संभव yहै। मेमोरी में रजिस्टर की तुलना में कम सटीकता हो सकती है, जिससे तुलना विफल हो सकती है।
डेविड श्वार्ट्ज

1
यह एक झूठे के लिए एक मामला हो सकता है, मैंने अभी तक ऐसा नहीं सोचा है। (जब से ओपी ने कोई विशेष मामले उपलब्ध नहीं कराए, मैं कोई अतिरिक्त अड़चन नहीं मान रहा हूं)
अनिर्बन 166

1
मैं वास्तव में नहीं समझ पा रहा हूं कि आप क्या कह रहे हैं। जैसा कि मैं इस प्रश्न को समझता हूं, ओपी पूछ रहा है कि क्या एक फ्लोट की नकल करना और फिर समानता के लिए परीक्षण करना सफल होने की गारंटी है। आपका उत्तर "हाँ" कह रहा है। मैं पूछ रहा हूं कि उत्तर क्यों नहीं है।
डेविड श्वार्ट्ज 5

6
संपादन इस उत्तर को गलत बनाता है। C ++ मानक के लिए आवश्यक है कि असाइनमेंट वैल्यू को डेस्टिनेशन टाइप में बदल दे- एक्सप्रेशन में एक्सटेंशन्स का ज्यादा इस्तेमाल किया जा सकता है, लेकिन असाइनमेंट को बरकरार नहीं रखा जा सकता। यह अपरिवर्तनीय है कि क्या मूल्य एक रजिस्टर या मेमोरी में आयोजित किया जाता है; C ++ मानक की आवश्यकता है, जैसा कि कोड लिखा गया है, floatअतिरिक्त परिशुद्धता के बिना एक मूल्य।
एरिक पोस्टपिसिल

2
@AProgrammer यह देखते हुए कि एक (n बेहद) बग्गी कंपाइलर सैद्धांतिक रूप int a=1; int b=a; assert( a==b );से जोर लगाने का कारण बन सकता है , मुझे लगता है कि यह केवल सही ढंग से काम करने वाले कंपाइलर के संबंध में इस प्रश्न का उत्तर देने के लिए समझ में आता है (जबकि संभवतः ध्यान दें कि कुछ टॉयलेटर्स के कुछ संस्करण / हैं) -बीज-ज्ञात-इस गलत पाने के लिए)। व्यावहारिक रूप से, यदि किसी कारण से एक संकलक एक पंजीकृत-संग्रहित असाइनमेंट के परिणाम से अतिरिक्त परिशुद्धता को नहीं हटाता है, तो उसे उस मूल्य का उपयोग करने से पहले ऐसा करना चाहिए ।
त्रिपुंड

-1

हां, यह सच है कि यह NaN को छोड़कर हमेशा वापस आ जाएगा । यदि वैरिएबल मान NaN है तो यह हमेशा गलत रिटर्न देता है !

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.