2014-10-12 13 views
28

C और C++ 2014 तक पूर्व के सभी संस्करणों में, लेखन1 << 31 क्यों कार्यान्वित किया गया-सी ++ 14 में परिभाषित किया गया?

1 << (CHAR_BIT * sizeof(int) - 1) 

वजह से अपरिभाषित व्यवहार है, क्योंकि बाएं स्थानांतरण 2 द्वारा लगातार गुणा के बराबर होने के रूप में परिभाषित किया गया है, और इस बदलाव का कारण बनता है पर हस्ताक्षर किए पूर्णांक अतिप्रवाह:

E1 << E2 का परिणाम E1E2 बिट स्थिति स्थानांतरित हो गया है; खाली बिट्स शून्य से भरे हुए हैं। [...] यदि E1 पर एक हस्ताक्षरित प्रकार और nonnegative मान है, और E1 × 2 ई 2 परिणाम प्रकार में प्रतिनिधित्व योग्य है, तो यह परिणामस्वरूप मान है; अन्यथा, व्यवहार अपरिभाषित है।

सी ++ 14 में

हालांकि पाठ << के लिए बदल गया है लेकिन गुणन के लिए नहीं:

E1 << E2 का मूल्य E1 बाएं स्थानांतरित कर दिया E2 बिट पदों है; खाली बिट्स शून्य से भरे हुए हैं। [...] अन्यथा, यदि E1 एक हस्ताक्षरित प्रकार और गैर-नकारात्मक मान, और E1 × 2 E2 है प्रदर्शनीय में परिणाम प्रकार की अहस्ताक्षरित प्रकार, तो उस मूल्य इसी है, परिणाम प्रकार के लिए परिवर्तित, परिणामी मूल्य है; अन्यथा, व्यवहार अपरिभाषित है।

व्यवहार अब हस्ताक्षर किए प्रकार के बाहर के रेंज काम के लिए के रूप में ही, यानी के रूप में द्वारा [conv.integral]/3 कवर किया गया है:

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

इसका मतलब है कि यह अभी भी 1 << 31 (32-बिट int के साथ सिस्टम पर) लिखने के लिए गैर पोर्टेबल है। तो सी ++ 14 में यह परिवर्तन क्यों किया गया था?

+1

+1 हॉवर्ड हिन्नेंट [इस विषय पर टिप्पणियां] (http://stackoverflow.com/questions/19593938/is-left-shifting-a-negative-integer-undefined-behavior-in-c11#comment29091986_19593938), मुझे याद रखने का एकमात्र कारण यह है कि टिप्पणी इस [प्रश्न] (http://stackoverflow.com/q/21319413/1708801) के लिए मेरी प्रेरणा का हिस्सा थी। –

+0

कल्पना कीजिए कि अगर उन्होंने इसके बजाय कुछ ऐसा कहा था: "बाधाएं: प्रचार के बाद, बाएं ऑपरेंड ई 1 एक हस्ताक्षरित पूर्णांक प्रकार होगा"। यह मानव जाति को सूक्ष्म, शिफ्ट से संबंधित बग की खगोलीय मात्रा से बचा लेगा। कि शब्दों की – Lundin

उत्तर

16

प्रासंगिक मुद्दा CWG 1457, जहां औचित्य परिवर्तन 1 << 31 निरंतर भाव में इस्तेमाल किया जा करने की अनुमति देता है कि है:

5,8 की वर्तमान शब्दों [expr.shift] पैरा 2 यह अनिर्धारित बनाता व्यवहार करने के लिए द्वारा दिए गए प्रकार का सबसे नकारात्मक पूर्णांक बनाने बाएं स्थानांतरण संकेत बिट में एक (हस्ताक्षरित) 1, भले ही इस अपूर्व किया नहीं है और (दुक्की-पूरक) आर्किटेक्चर के बहुमत पर सही ढंग से काम करता है:

... यदि ई 1 के पास एक हस्ताक्षरित प्रकार और गैर-ऋणात्मक मान है, और E1 * 2 E2 परिणाम प्रकार में प्रतिनिधित्व योग्य है, तो यह परिणामस्वरूप मान है; अन्यथा, व्यवहार अपरिभाषित है।

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

निरंतर अभिव्यक्ति में अपरिभाषित व्यवहार नहीं हो सकता है, जिसका अर्थ है कि एक संदर्भ में यूबी युक्त एक अभिव्यक्ति का उपयोग निरंतर अभिव्यक्ति की आवश्यकता होती है जिससे कार्यक्रम खराब हो जाता है। libstdC++ के numeric_limits::min, उदाहरण के लिए, once failed to compile in clang इसके कारण।

+0

लेखक उलझन में लगता है ... नया नियम एक या तो "साइन बिट में से बाएं स्थानांतरण एक (हस्ताक्षरित) 1 दिए गए प्रकार का सबसे नकारात्मक पूर्णांक बनाने के" करने के लिए अनुमति नहीं है। नतीजा कार्यान्वयन-परिभाषित है, जो सबसे नकारात्मक मूल्य देने की गारंटी होने से बहुत रोना है। –

+0

@BenVoigt दो के पूरक के लिए केवल एक सौ परिणाम है। और यह लगातार सभी प्रासंगिक और सामान्य कंपाइलरों में लागू किया जाता है। – Columbo

+2

@Loopunroller: प्रश्न में पिछले स्टैंडर्ड बोली कि जिस तरह से हस्ताक्षर किए पालन two's-पूरक नियमों के एक अहस्ताक्षरित मूल्य की है कि रूपांतरण की आवश्यकता होती है से बचने के लिए शब्दों में है। साथ ही, यह सच नहीं है कि दो के पूरक के लिए केवल एक सौ परिणाम है। संतृप्त अंकगणित भी एक उपयोगी, सौहार्दपूर्ण विकल्प है। कोई यह भी कह सकता है कि सकारात्मक मूल्य को दोगुना करने से नकारात्मक है। –

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