इंटेल C ++ कंपाइलर के साथ NaN - NaN == 0.0 क्यों होता है?


300

यह सर्वविदित है कि NaNs अंकगणित में प्रचारित करते हैं, लेकिन मुझे कोई प्रदर्शन नहीं मिला, इसलिए मैंने एक छोटा लेख लिखा:

#include <limits>
#include <cstdio>

int main(int argc, char* argv[]) {
    float qNaN = std::numeric_limits<float>::quiet_NaN();

    float neg = -qNaN;

    float sub1 = 6.0f - qNaN;
    float sub2 = qNaN - 6.0f;
    float sub3 = qNaN - qNaN;

    float add1 = 6.0f + qNaN;
    float add2 = qNaN + qNaN;

    float div1 = 6.0f / qNaN;
    float div2 = qNaN / 6.0f;
    float div3 = qNaN / qNaN;

    float mul1 = 6.0f * qNaN;
    float mul2 = qNaN * qNaN;

    printf(
        "neg: %f\nsub: %f %f %f\nadd: %f %f\ndiv: %f %f %f\nmul: %f %f\n",
        neg, sub1,sub2,sub3, add1,add2, div1,div2,div3, mul1,mul2
    );

    return 0;
}

उदाहरण ( यहां लाइव चल रहा है ) मूल रूप से वही उत्पन्न करता है जो मैं उम्मीद करता हूं (नकारात्मक थोड़ा अजीब है, लेकिन यह एक तरह से समझ में आता है):

neg: -nan
sub: nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan

MSVC 2015 कुछ इसी तरह का उत्पादन करता है। हालाँकि, Intel C ++ 15 का उत्पादन करता है:

neg: -nan(ind)
sub: nan nan 0.000000
add: nan nan
div: nan nan nan
mul: nan nan

विशेष रूप से, qNaN - qNaN == 0.0

यह ... सही, सही नहीं हो सकता? प्रासंगिक मानकों (आईएसओ सी, आईएसओ सी ++, आईईईई 754) इस बारे में क्या कहते हैं, और संकलक के बीच व्यवहार में अंतर क्यों है?


18
जावास्क्रिप्ट और पायथन (सुन्न) में यह व्यवहार नहीं है। Nan-NaNहै NaN। पर्ल और स्काला भी इसी तरह का व्यवहार करते हैं।
पॉल

33
हो सकता है कि आपने असुरक्षित गणित अनुकूलन ( -ffast-mathgcc के बराबर ) को सक्षम किया हो?
मैटियो इटालिया

5
@nm: सच नहीं है। अनुलग्नक एफ, जो वैकल्पिक है, लेकिन समर्थित होने पर आदर्श है, और सभी पर निर्दिष्ट फ्लोटिंग पॉइंट व्यवहार के लिए आवश्यक है, अनिवार्य रूप से IEEE 754 को C.
R में शामिल करता है। GitHub STOP HELPING ICE

5
यदि आप IEEE 754 मानक के बारे में पूछना चाहते हैं, तो प्रश्न में इसका कहीं उल्लेख करें।
एन। 'सर्वनाम' मी।

68
मुझे यकीन था कि यह सवाल शीर्षक से जावास्क्रिप्ट के बारे में था।
माइकइलियार

जवाबों:


300

डिफ़ॉल्ट चल बिन्दु इंटेल C ++ कम्पाइलर में से निपटने है /fp:fast, जो हैंडल NaNके unsafely (यह भी है, जिसमें परिणाम NaN == NaNजा रहा है trueउदाहरण के लिए)। निर्दिष्ट करने का प्रयास करें /fp:strictया /fp:preciseदेखें कि क्या मदद करता है।


15
मैं बस यही कोशिश कर रहा था। दरअसल, सटीक या सख्त निर्दिष्ट करने से समस्या ठीक हो जाती है।
5

67
मैं इंटेल के निर्णय को डिफ़ॉल्ट रूप से समर्थन करना चाहता हूं /fp:fast: यदि आप कुछ सुरक्षित चाहते हैं , तो आपको शायद पहले स्थान पर NaNs को चालू करने से बेहतर बचना चाहिए, और आमतौर पर ==फ़्लोटिंग-पॉइंट नंबरों के साथ उपयोग नहीं करना चाहिए । IEEE754 ने NaN को जो अजीब शब्दार्थ दिया है, उस पर भरोसा करना मुसीबत के लिए पूछ रहा है।
लेफ्टरनैबाउट

10
@leftaroundabout: क्या आप NaN के बारे में अजीब लग रहे हैं, एक तरफ IMHO के भयानक निर्णय से NaN! = NaN वापसी सही है?
सुपरकैट

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

6
@supercat जिज्ञासा से, क्या आप NaN==NaNवापसी के निर्णय से सहमत हैं false?
काइल स्ट्रैंड

53

इस । । । सही नहीं हो सकता, ठीक है? मेरा प्रश्न: प्रासंगिक मानकों (आईएसओ सी, आईएसओ सी ++, आईईईई 754) इस बारे में क्या कहते हैं?

पेट्र अब्दुलिन ने पहले ही जवाब दिया कि संकलक एक 0.0जवाब क्यों देता है ।

यहाँ IEEE-754: 2008 कहता है:

(6.2 संचालन NaNs के साथ) "[...] शांत NaN आदानों के साथ एक ऑपरेशन के लिए, अधिकतम और न्यूनतम संचालन के अलावा अन्य, अगर एक अस्थायी-बिंदु परिणाम दिया जाना है तो परिणाम एक शांत NaN होगा जो एक होना चाहिए इनपुट NaNs। "

तो दो शांत NaN ऑपरेंड के घटाव के लिए एकमात्र वैध परिणाम एक शांत NaN है; कोई अन्य परिणाम मान्य नहीं है।

सी मानक कहते हैं:

(C11, F.9.2 अभिव्यक्ति रूपांतरण p1) "[...]

x - x → 0. 0 "एक्स - एक्स और 0. 0 एक्सप्रेशंस समतुल्य नहीं हैं यदि x एक NaN या अनंत है"

(जहां यहां NaN F.2.1p1 के अनुसार एक शांत NaN को दर्शाता है "यह विनिर्देश सिग्नल NaN के व्यवहार को परिभाषित नहीं करता है। यह आमतौर पर NaN को शांत करने के लिए NaN शब्द का उपयोग करता है")


20

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

$ g++ -O2 test.cc && ./a.out 
neg: -nan
sub: nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan

$ clang++ -O2 test.cc && ./a.out 
neg: -nan
sub: -nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan

- लेकिन अगर आप शुद्धता की कीमत पर गति पूछते हैं, तो आपको वह मिलता है जो आप माँगते हैं -

$ g++ -O2 -ffast-math test.cc && ./a.out 
neg: -nan
sub: nan nan 0.000000
add: nan nan
div: nan nan 1.000000
mul: nan nan

$ clang++ -O2 -ffast-math test.cc && ./a.out 
neg: -nan
sub: -nan nan 0.000000
add: nan nan
div: nan nan nan
mul: nan nan

मुझे लगता है कि आईसीसी के डिफ़ॉल्ट के विकल्प की आलोचना करना पूरी तरह से उचित है , लेकिन मैं पूरे यूनिक्स युद्धों को उस निर्णय में वापस नहीं पढ़ूंगा।


ध्यान दें कि -ffast-math, gccआईएसओ 9899 का अनुपालन नहीं कर रहा है: 2011 में अस्थायी बिंदु अंकगणित के संबंध में।
fuz

1
@FUZxxl हां, मुद्दा यह है कि दोनों कंपाइलरों में एक गैर-फ्लोटिंग फ़्लोटिंग-पॉइंट मोड है, यह सिर्फ उस मोड के लिए icc डिफ़ॉल्ट है और gcc नहीं करता है।
zwol

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