सी ++ चेतावनी: शून्य से दोगुना का विभाजन


98

मामला एक:

#include <iostream>

int main()
{
    double d = 15.50;
    std::cout<<(d/0.0)<<std::endl;
}

यह बिना किसी चेतावनी और प्रिंट के संकलन करता है inf। ठीक है, सी ++ शून्य से विभाजन को संभाल सकता है, ( इसे लाइव देखें )।

परंतु,

केस 2:

#include <iostream>

int main()
{
    double d = 15.50;
    std::cout<<(d/0)<<std::endl;
}

संकलक निम्नलिखित चेतावनी देता है ( इसे लाइव देखें ):

warning: division by zero [-Wdiv-by-zero]
     std::cout<<(d/0)<<std::endl;

संकलक दूसरे मामले में चेतावनी क्यों देता है?

है 0 != 0.0?

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

#include <iostream>

int main()
{
    if(0 == 0.0)
        std::cout<<"Same"<<std::endl;
    else
        std::cout<<"Not same"<<std::endl;
}

उत्पादन:

Same

9
मुझे लगता है कि यह दूसरे मामले में एक पूर्णांक के रूप में शून्य लेता है और एक चेतावनी छोड़ देता है, भले ही गणना बाद में डबल का उपयोग करके की जाएगी (जो मुझे लगता है कि जब एक डबल होता है तो व्यवहार होना चाहिए)।
Qubit

10
यह वास्तव में एक क्यूओआई मुद्दा है। न तो चेतावनी या चेतावनी की कमी सी ++ मानक द्वारा ही अनिवार्य है। क्या आप जीसीसी का उपयोग कर रहे हैं?
स्टोरीटेलर - Unslander Monica


5
@StoryTeller QoI क्या है? en.wikipedia.org/wiki/QoI ?
user202729

5
अपने अंतिम प्रश्न के संबंध में, "0 0 0.0 के समान है?" उत्तर है मान समान हैं, लेकिन जैसा कि आपको पता चला है, इसका मतलब यह नहीं है कि वे समान हैं। विभिन्न प्रकार! जैसे 'A' 65 के समान नहीं है।
श्री लिस्टर

जवाबों:


108

शून्य से बिंदु विभाजन फ्लोटिंग है अच्छी तरह से आईईईई द्वारा परिभाषित (अंश के मूल्य के अनुसार तो सकारात्मक या नकारात्मक और अनंत देता है या ( NaN± 0 के लिए) )।

पूर्णांकों के लिए, अनंत का प्रतिनिधित्व करने का कोई तरीका नहीं है और भाषा ऑपरेशन को अपरिभाषित करने के लिए परिभाषित करती है इसलिए कंपाइलर उस पथ से स्पष्ट रूप से आपको हटाने की कोशिश करता है।

हालाँकि, इस मामले में, चूंकि अंश एक है double, इसलिए विभाजक ( 0) को एक डबल में भी बढ़ावा दिया जाना चाहिए और यहां कोई चेतावनी देने का कोई कारण नहीं है, जबकि चेतावनी न देने के लिए 0.0मुझे लगता है कि यह एक कंपाइलर बग है।


8
दोनों हालांकि अस्थायी बिंदु विभाजन हैं। में d/0, 0के प्रकार में परिवर्तित किया जाता है d

43
ध्यान दें कि C ++ को IEEE 754 (भले ही मैंने एक अलग मानक का उपयोग करते हुए कभी नहीं देखा है) का उपयोग करने की आवश्यकता नहीं है ..
Yksisarvinen

1
@hvd, अच्छी बात है, ऐसा लगता है कि यह मामला एक कंपाइलर बग की तरह दिखता है
मोटी

14
मैं सहमत हूं कि इसे या तो दोनों मामलों में चेतावनी दी जानी चाहिए, या दोनों मामलों में चेतावनी नहीं दी जानी चाहिए (यह निर्भर करता है कि संकलक शून्य से फ्लोटिंग डिवीजन की हैंडलिंग क्या है)
एमएम

8
बहुत यकीन है कि शून्य से फ़्लोटिंग पॉइंट डिवीजन भी यूबी है - यह सिर्फ यह है कि जीसीसी आईईईई 754 के अनुसार इसे लागू करता है। हालांकि उन्हें ऐसा करने की ज़रूरत नहीं है।
मार्टिन बोनर

42

मानक C ++ में, दोनों मामले अपरिभाषित व्यवहार हैं । आपकी हार्ड ड्राइव को प्रारूपित करने सहित कुछ भी हो सकता है। आपको "रिटर्न inf। ओके" या किसी अन्य व्यवहार पर उम्मीद या भरोसा नहीं करना चाहिए।

संकलक स्पष्ट रूप से एक मामले में चेतावनी देने का फैसला करता है और दूसरे को नहीं, लेकिन इसका मतलब यह नहीं है कि एक कोड ठीक है और एक नहीं है। यह संकलक की पीढ़ी के चेतावनियों का एक विचित्र उदाहरण है।

C ++ 17 मानक से [expr.mul] / 4:

बाइनरी /ऑपरेटर भागफल की पैदावार करता है, और बाइनरी %ऑपरेटर दूसरी द्वारा पहली अभिव्यक्ति के विभाजन से शेष पैदावार देता है। यदि दूसरा ऑपरेंड /या %शून्य है व्यवहार अपरिभाषित है।


21
सच नहीं है, फ्लोटिंग पॉइंट में अंकगणित का विभाजन शून्य से अच्छी तरह से परिभाषित है।
मत्ती

9
@Motti - यदि कोई खुद को C ++ मानक तक सीमित करता है, तो ऐसी कोई गारंटी नहीं है। इस प्रश्न का दायरा स्पष्ट रूप से निर्दिष्ट नहीं है।
स्टोरीटेलर - Unslander मोनिका

9
@StoryTeller मैं बहुत यकीन है (हालांकि मैं मानक ही इस बात के लिए दस्तावेज़ को देखा नहीं किया है) करता है, तो यह है कि std::numeric_limits<T>::is_iec559है trueशून्य के लिए से विभाजन, तो Tयूबी (और नहीं है सबसे प्लेटफार्मों यह है पर trueके लिए doubleऔर floatहै, हालांकि पोर्टेबल होने के लिए आप करेंगे यह स्पष्ट रूप से जांचने की आवश्यकता है कि ifया if constexpr)।
डैनियल एच

6
@ दानिएल - "वाजिब" वास्तव में काफी व्यक्तिपरक है। यदि यह सवाल भाषा-वकील को टैग किया गया था तो उचित मान्यताओं का एक पूरा (बहुत छोटा) सेट होगा।
स्टोरीटेलर - Unslander Monica

5
@ एमएम याद रखें कि यह वही है जो आपने कहा था, लेकिन इससे अधिक कुछ नहीं: अपरिभाषित का मतलब यह नहीं है कि कोई आवश्यकताएं नहीं लगाई गई हैं, इसका मतलब है कि मानक द्वारा कोई आवश्यकताएं लागू नहीं की गई हैं । मेरा तर्क है कि इस मामले में, एक कार्यान्वयन के is_iec559रूप में परिभाषित करने वाला तथ्य यह trueहै कि कार्यान्वयन व्यवहार का दस्तावेजीकरण कर रहा है जो मानक को छोड़ देता है। यह सिर्फ इतना है कि यह एक ऐसा मामला है जहां कार्यान्वयन के दस्तावेज को प्रोग्रामेटिक रूप से पढ़ा जा सकता है। केवल एक ही नहीं: is_moduloहस्ताक्षरित पूर्णांक प्रकारों के लिए भी यही लागू होता है ।

12

इस विशेष प्रश्न का उत्तर देने के लिए मेरा सबसे अच्छा अनुमान यह होगा कि संकलक रूपांतरण करने से पहले चेतावनी का उत्सर्जन करता intहै double

तो, चरण इस प्रकार होंगे:

  1. पार्स अभिव्यक्ति
  2. अंकगणितीय ऑपरेटर /(T, T2) , जहां T=double, T2=int
  3. जांचें कि std::is_integral<T2>::valueयह क्या है trueऔर b == 0यह चेतावनी को ट्रिगर करता है।
  4. चेतावनी से बाहर निकलें
  5. का अंतर्निहित रूपांतरण T2करेंdouble
  6. अच्छी तरह से परिभाषित विभाजन करें (क्योंकि कंपाइलर ने IEEE 754 का उपयोग करने का निर्णय लिया है)।

यह निश्चित रूप से अटकलें हैं और संकलक-परिभाषित विनिर्देशों पर आधारित है। मानक दृष्टिकोण से, हम संभव अपरिभाषित व्यवहार से निपट रहे हैं।


कृपया ध्यान दें कि यह जीसीसी प्रलेखन
(btw) के अनुसार अपेक्षित व्यवहार है । ऐसा लगता है कि इस ध्वज का जीसीसी 8.1 में स्पष्ट रूप से उपयोग नहीं किया जा सकता है)

-Wdiv-by-zero
चेतावनी संकलन-समय पूर्णांक विभाजन शून्य के बारे में। यह डिफ़ॉल्ट है। चेतावनी संदेशों को बाधित करने के लिए, -Wno-div-by-zero का उपयोग करें। शून्य से फ्लोटिंग पॉइंट डिवीजन के बारे में चेतावनी नहीं दी जाती है, क्योंकि यह शिशुओं और NaN को प्राप्त करने का एक वैध तरीका हो सकता है।


2
यह नहीं है कि C ++ कंपाइलर कैसे काम करता है। कंपाइलर को /यह पता करने के लिए कि यह विभाजन है, अधिभार रिज़ॉल्यूशन निष्पादित करना है। यदि बाएं हाथ की ओर एक Fooवस्तु होती, और एक होती operator/(Foo, int), तो वह विभाजन भी नहीं होती। कंपाइलर केवल यह जानता है कि यह विभाजन है जब उसने built-in / (double, double)दाएं हाथ की ओर एक अंतर्निहित रूपांतरण का उपयोग करके चुना है । लेकिन इसका मतलब है कि यह एक विभाजन नहीं कर रहा है int(0), यह एक विभाजन कर रहा है double(0)
MSalters

@MSalters कृपया इसे देखें। C ++ के बारे में मेरा ज्ञान सीमित है, लेकिन संदर्भ के अनुसार operator /(double, int)निश्चित रूप से स्वीकार्य है। फिर, यह कहता है कि रूपांतरण किसी अन्य कार्रवाई से पहले किया जाता है, लेकिन जीसीसी एक त्वरित जांच में निचोड़ सकता है यदि T2पूर्णांक प्रकार है और b == 0यदि ऐसा है तो चेतावनी का उत्सर्जन करें। सुनिश्चित नहीं है कि यह पूरी तरह से मानक अनुपालन है, लेकिन संकलक को चेतावनी को परिभाषित करने में पूरी स्वतंत्रता है और जब उन्हें ट्रिगर किया जाना चाहिए।
यक्सीसारविनन

2
हम यहां बिल्ट-इन ऑपरेटर के बारे में बात कर रहे हैं। यह एक अजीब बात है। यह वास्तव में एक फ़ंक्शन नहीं है, इसलिए आप इसका पता नहीं लगा सकते। इसलिए आप निर्धारित नहीं कर सकते कि क्या operator/(double,int)वास्तव में मौजूद है। उदाहरण के लिए संकलक इसे बदलने के साथ a/bनिरंतर के लिए अनुकूलित करने का निर्णय bले सकता है a * (1/b)। बेशक, इसका मतलब है कि आप अब operator/(double,double)रनटाइम पर कॉल नहीं कर रहे हैं , लेकिन तेजी से operator*(double,double)। लेकिन यह अब ऑप्टिमाइज़र है जो यात्राएं करता है 1/0, लगातार इसे खिलाना होगाoperator*
जूलर्स

@MSalters आम तौर पर फ्लोटिंग पॉइंट डिवीजन को गुणन के साथ नहीं बदला जा सकता है, शायद असाधारण मामलों से अलग, जैसे कि 2.
user202729

2
@ user202729: जीसीसी पूर्णांक विभाजन के लिए भी करता है । एक पल के लिए उसे डूबने दें। जीसीसी पूर्णांक विभाजन को पूर्णांक गुणा के साथ बदलता है। हां, यह संभव है, क्योंकि जीसीसी को पता है कि यह एक अंगूठी पर काम कर रहा है (संख्याएं
मोडुलो

9

मैं इस उत्तर में यूबी / नहीं यूबी पराजय में नहीं जाऊंगा।

मैं सिर्फ इतना है कि इंगित करना चाहते हैं 0और 0.0 अलग हैं के बावजूद 0 == 0.0सच करने के लिए मूल्यांकन। 0एक intशाब्दिक है और 0.0एक doubleशाब्दिक है।

हालांकि इस मामले में अंतिम परिणाम समान है: d/0फ्लोटिंग पॉइंट डिवीजन है क्योंकि dडबल है और इसलिए 0अंतर्निहित रूप से डबल में बदल दिया गया है।


5
मैं यह नहीं देखता कि यह कैसे प्रासंगिक है, यह देखते हुए कि सामान्य अंकगणितीय रूपांतरण यह निर्दिष्ट करते हैं कि एक doubleऐसे intमाध्यम से विभाजित किया जाता है जिसे intपरिवर्तित किया जाता है double , और यह उस मानक में निर्दिष्ट होता है जो (conv.fpint / 2) में 0परिवर्तित होता है0.0
MM

@MM ओपी जानना चाहता है कि क्या 0वही है0.0
बोलोव

2
प्रश्न कहता है "है 0 != 0.0?"। ओपी कभी नहीं पूछता कि क्या वे "समान" हैं। मुझे यह भी लगता है कि प्रश्न का आशय इस बात से है कि क्या d/0अलग-अलग व्यवहार कर सकते हैंd/0.0
एमएम

2
@MM - ओपी ने पूछा । वे वास्तव में उन स्थिरांक संपादन के साथ सभ्य एसओ नेटिकट नहीं दिखा रहे हैं।
स्टोरीटेलर - Unslander Monica

7

मुझे लगता है कि लोगों का तर्क था foo/0और foo/0.0कर रहे हैं नहीं एक ही। अर्थात्, पहले (पूर्णांक विभाजन या फ्लोटिंग पॉइंट डिवीजन) का परिणामी प्रभाव अत्यधिक प्रकार पर निर्भर करता है foo, जबकि दूसरे के लिए भी यह सच नहीं है (यह हमेशा फ्लोटिंग पॉइंट डिवीजन होगा)।

चाहे दोनों में से कोई भी यूबी अप्रासंगिक हो। मानक का हवाला देते हुए:

अनुज्ञेय अपरिभाषित व्यवहार की स्थिति की अनदेखी पूरी तरह से अप्रत्याशित परिणामों के साथ होती है, अनुवाद या कार्यक्रम निष्पादन के दौरान व्यवहार करने के लिए पर्यावरण की विशेषता का एक दस्तावेज तरीके से (नैदानिक ​​संदेश जारी करने के साथ या बिना) , अनुवाद या निष्पादन को समाप्त करने (जारी करने के साथ)। नैदानिक ​​संदेश का)।

(जोर मेरा)

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


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

@ShafikYaghmour आप उत्तर में बिंदु से चूक गए। ध्यान दें कि मैंने कभी उल्लेख नहीं किया कि किस प्रकार का है foo। वह जानबूझकर है। आपका दावा केवल उस मामले में सच है जो fooएक अस्थायी बिंदु प्रकार है।
कैसियो रेनन

मैंने नहीं किया, संकलक के पास जानकारी है और रूपांतरणों को समझते हैं, शायद एक पाठ आधारित स्थिर विश्लेषक को ऐसी चीजों द्वारा पकड़ा जा सकता है लेकिन संकलक को नहीं होना चाहिए।
शाफिक याघमोर

मेरा कहना है कि हाँ, संकलक सामान्य अंकगणितीय रूपांतरणों को जानता है, लेकिन यह प्रोग्रामर के स्पष्ट होने पर चेतावनी जारी नहीं करने का विकल्प चुनता है। पूरे बिंदु यह है कि यह शायद एक बग नहीं है, लेकिन इसके बजाय यह जानबूझकर व्यवहार है।
कैसियो रेनन

फिर मैंने जो दस्तावेज़ बताया वह गलत है, क्योंकि दोनों मामले फ़्लोटिंग-पॉइंट डिवीज़न हैं। तो या तो दस्तावेज गलत है या निदान में बग है।
शफिक याघमोर

4

यह एक gcc बग की तरह दिखता है, जो -Wno-div-by-zero स्पष्ट रूप से कहता है कि प्रलेखन :

संकलन-समय पूर्णांक विभाजन के बारे में शून्य द्वारा चेतावनी न दें। शून्य से फ्लोटिंग-पॉइंट डिवीजन के बारे में चेतावनी नहीं दी जाती है, क्योंकि यह शिशुओं और NaN को प्राप्त करने का एक वैध तरीका हो सकता है।

और [] expr.arith.conv] में कवर किए गए सामान्य अंकगणितीय रूपांतरणों के बाद दोनों ऑपरेंड दोहरे होंगे :

कई बाइनरी ऑपरेटर्स जो कि अंकगणित या एन्यूमरेशन प्रकार के ऑपरेंड की उम्मीद करते हैं, एक समान तरीके से रूपांतरण और परिणाम परिणाम टाइप करते हैं। उद्देश्य एक सामान्य प्रकार की उपज है, जो परिणाम का प्रकार भी है। इस पैटर्न को सामान्य अंकगणितीय रूपांतरण कहा जाता है, जिसे निम्नानुसार परिभाषित किया गया है:

...

- अन्यथा, यदि या तो ऑपरेंड डबल है, तो दूसरे को डबल में बदल दिया जाएगा।

और [expr.mul] :

* और / और के ऑपरेंड में अंकगणित या अनकैप्ड एन्यूमरेशन प्रकार होंगे; % के परिचालनों में अभिन्न या अनकैप्ड एन्यूमरेशन प्रकार होगा। सामान्य अंकगणितीय रूपांतरण ऑपरेंड पर किए जाते हैं और परिणाम के प्रकार को निर्धारित करते हैं।

इस बात के संबंध में कि क्या फ़्लोटिंग पॉइंट जीरो से विभाजित है अपरिभाषित व्यवहार है और इसके साथ अलग-अलग कार्यान्वयन सौदा मेरा उत्तर कैसे लगता हैटी एल; डॉ; ऐसा प्रतीत होता है कि जीसीई एफ एफटी के अनुरूप फ्लोटिंग पॉइंट डिवाइड को शून्य से विभाजित करता है, इसलिए अपरिभाषित यहां एक भूमिका नहीं निभाता है। क्लैंग के लिए उत्तर अलग होगा।


2

शून्य से फ्लोटिंग पॉइंट डिवीजन शून्य से पूर्णांक विभाजन की तुलना में अलग व्यवहार करता है।

आईईईई चल बिन्दु + inf और -inf के बीच मानक differentiates, जबकि पूर्णांकों अनंत की दुकान नहीं कर सकते हैं। शून्य परिणाम द्वारा पूर्णांक विभाजन अपरिभाषित व्यवहार है। शून्य से फ्लोटिंग पॉइंट डिवीजन को फ्लोटिंग पॉइंट मानक द्वारा परिभाषित किया गया है और परिणाम + inf या -inf में हैं।


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