2011-06-07 14 views
7

आज मैं पर नज़र रखने गया था क्यों मेरे कार्यक्रम कुछ अप्रत्याशित चेकसम-बेमेल त्रुटियों हो रही थी, कुछ कोड में है कि मैं ने लिखा है कि serializes और deserializes आईईईई-754 फ्लोटिंग प्वाइंट मूल्यों, एक प्रारूप है कि एक 32-बिट चेकसम मान शामिल में (जो फ़्लोटिंग-पॉइंट सरणी के बाइट्स पर एक सीआरसी-प्रकार एल्गोरिदम चलाकर गणना की जाती है)।सी == ऑपरेटर कैसे तय करता है कि दो फ़्लोटिंग पॉइंट मान बराबर हैं या नहीं?

कुछ सिर-खरोंच के बाद, मुझे एहसास हुआ कि समस्या 0.0f और -0.0f में अलग-अलग बिट-पैटर्न (क्रमशः 0x00000000 बनाम 0x00000080 (थोड़ा-अंत)) है, लेकिन उन्हें C++ के बराबर माना जाता है समानता ऑपरेटर। इसलिए, चेकसम-मिस्चैच त्रुटियां इसलिए हुईं क्योंकि मेरे चेकसम-गणना एल्गोरिदम ने उन दो बिट-पैटर्न के बीच अंतर उठाया, जबकि मेरे कोडबेस के कुछ अन्य हिस्सों (जो फ़्लोटिंग पॉइंट समानता परीक्षण का उपयोग करते हैं, मूल्यों को देखने के बजाय बाइट-बाय- बाइट) ने उस भेद को नहीं बनाया।

ठीक है, निष्पक्ष पर्याप्त - मैं शायद से फ्लोटिंग प्वाइंट समानता वैसे भी परीक्षण करना बेहतर पता होना चाहिए था।

लेकिन यह मिल गया मुझे सोच, वहाँ अन्य आईईईई-754 चल बिन्दु मान जो बराबर माना जाता है लेकिन (सी == ऑपरेटर के अनुसार) विभिन्न बिट पैटर्न है कर रहे हैं? या, इसे एक और तरीका रखने के लिए, == ऑपरेटर कैसे तय करता है कि दो फ़्लोटिंग-पॉइंट मान बराबर हैं या नहीं? मुझे नौसिखिया हालांकि यह memcmp() जैसे उनके बिट-पैटर्न पर कुछ कर रहा था, लेकिन स्पष्ट रूप से यह उससे अधिक प्रचलित है।

यहाँ मैं क्या मतलब है की एक कोड उदाहरण है, इस मामले में मैं ऊपर स्पष्ट नहीं किया गया था।

#include <stdio.h> 

static void PrintFloatBytes(const char * title, float f) 
{ 
    printf("Byte-representation of [%s] is: ", title); 
    const unsigned char * p = (const unsigned char *) &f; 
    for (int i=0; i<sizeof(f); i++) printf("%02x ", p[i]); 
    printf("\n"); 
} 

int main(int argc, char ** argv) 
{ 
    const float pzero = -0.0f; 
    const float nzero = +0.0f; 
    PrintFloatBytes("pzero", pzero); 
    PrintFloatBytes("nzero", nzero); 
    printf("Is pzero equal to nzero? %s\n", (pzero==nzero)?"Yes":"No"); 
    return 0; 
} 
+0

http://how-to.wikia.com/wiki/Howto_compare_floating_point_numbers_in_the_C_programming_language FYI करें, एक एप्सिलॉन का उपयोग कर नाव तुलना के लिए आगे रास्ता है। विषय पर नहीं, लेकिन जानना उपयोगी है। – darvids0n

+1

NaNs किसी भी तरह से जा सकता है (संभवतः कंपाइलर के आधार पर)। वे मेमोरी में भी भिन्न हो सकते हैं क्योंकि बड़ी संख्या में संभावित नाएन (सिंगल परिशुद्धता के लिए 2^24-1) हैं। – ughoavgfhw

उत्तर

13

यह आईईईई-754 समानता नियमों का उपयोग हो सकता है।

  • -0 == +0
  • NaN != NaN
+2

+1 NaN। ध्यान दें कि यह एक ऐसा मामला है जहां बिट पैटर्न * समान * होंगे, लेकिन '==' 'false' वापस आ जाएगा। –

+1

+1 'NaN'। यह भी ध्यान रखें कि आपके पास दो 'NaN' का प्रतिनिधित्व करने वाले विभिन्न बिट पैटर्न हो सकते हैं। – trutheality

+0

वास्तव में, +0.0 और -0.0 केवल दो विशिष्ट बिट पैटर्न हैं जो बराबर तुलना कर सकते हैं। –

1

सटीक तुलना। यही कारण है कि फ्लोट पर एक परीक्षण के रूप में == से बचने के लिए सबसे अच्छा है। यह अप्रत्याशित और सूक्ष्म बग का कारण बन सकता है।

एक मानक उदाहरण इस कोड है:

float f = 0.1f; 

if((f*f) == 0.01f) 
    printf("0.1 squared is 0.01\n"); 
else 
    printf("Surprise!\n"); 

क्योंकि 0.1 बाइनरी (यह एक दोहरा रहा है जो कुछ भी नरक आप एक भिन्नात्मक द्विआधारी कहते हैं) 0.1*0.1 बिल्कुल 0.01 नहीं होगा में ठीक नहीं दर्शाया जा सकता - और इस प्रकार समानता परीक्षण काम नहीं करेगा।

संख्यात्मक विश्लेषकों विस्तार से इस बारे में चिंता है, लेकिन एक पहली सन्निकटन के लिए यह एक मूल्य को परिभाषित करने के लिए उपयोगी है - जो है से कितने करीब दो तैरता बराबर में स्वीकार किया गया की जरूरत है - एपीएल यह फ़ज़्ज़ कहा जाता है। तो तुम, उदाहरण के लिए, #define FUZZ 0.00001f और परीक्षण

float f = 0.1f; 

if(abs((f*f)-0.01f) < FUZZ) 
    printf("0.1 squared is 0.01\n"); 
else 
    printf("Surprise!\n"); 
+6

स्पष्ट रूप से यह बिट-बाय-बिट स्तर पर सटीक तुलना नहीं है, या यह इंगित नहीं करेगा कि (-0.0f == 0.0f) क्योंकि उन दो मानों में अलग-अलग बिट-पैटर्न हैं। –

+0

एक उचित बिंदु जो अधिक एपोजिट होगा, मैंने "थोड़ा तुलना करके सटीक बिट" लिखा था। –

2

विंडोज प्लेटफार्म के लिए, this link has:

  • फूट डालो 0 से पैदा करता है +/- INF, 0/0 जो NaN में जो परिणाम को छोड़कर।
  • का लॉग (+/-) 0 -INF पैदा करता है। ऋणात्मक मूल्य का लॉग (-0 के अलावा) NaN उत्पन्न करता है।
  • नकारात्मक संख्या के पारस्परिक वर्ग रूट (आरएसक्यू) या वर्ग रूट (वर्ग) NaN उत्पन्न करता है। अपवाद है -0; sqrt (-0) उत्पादन -0, और rsq (-0) उत्पादन -INF उत्पन्न करता है।
  • INF - INF = NaN
  • (+/-) INF/(+/-) INF = NaN
  • (+/-) INF * 0 = NaN
  • NaN (किसी भी ओ पी) किसी भी-मूल्य = NaN
  • तुलना ईक्यू, जीटी, जीई, एलटी, और ली, जब दोनों या दोनों ऑपरेटरों NaN रिटर्न गलत है।
  • तुलना 0 के संकेत को अनदेखा करती है (इसलिए +0 बराबर -0)।
  • तुलना एनई, जब दोनों या दोनों ऑपरेटरों NaN रिटर्न सत्य है।
  • +/- आईएनएफ के खिलाफ किसी भी गैर-एनएएन मूल्य की तुलना सही परिणाम लौटाती है।
संबंधित मुद्दे