C में, शिफ्ट ऑपरेटर ( <<
, >>
) अंकगणित या तार्किक हैं?
C में, शिफ्ट ऑपरेटर ( <<
, >>
) अंकगणित या तार्किक हैं?
जवाबों:
K & R 2nd संस्करण के अनुसार , परिणाम क्रियान्वित-निर्भर हस्ताक्षरित मूल्यों की सही पारियों के लिए हैं।
विकिपीडिया कहता है कि C / C ++ 'आमतौर पर हस्ताक्षरित मूल्यों पर एक अंकगणितीय बदलाव को लागू करता है।
मूल रूप से आपको या तो अपने कंपाइलर का परीक्षण करना होगा या उस पर भरोसा नहीं करना चाहिए। वर्तमान MS C ++ कंपाइलर के लिए मेरा VS2008 मदद कहता है कि उनका कंपाइलर एक अंकगणितीय बदलाव करता है।
जब बाईं ओर स्थानांतरण होता है, तो अंकगणित और तार्किक बदलाव के बीच कोई अंतर नहीं होता है। जब सही तरीके से शिफ्ट किया जाता है, तो शिफ्ट का प्रकार उस मान के प्रकार पर निर्भर करता है जिसे स्थानांतरित किया जा रहा है।
(जैसा कि उन पाठकों के लिए पृष्ठभूमि अंतर से अपरिचित है, 1 बिट द्वारा "लॉजिकल" राइट शिफ्ट सभी बिट्स को दाईं ओर ले जाती है और बाईं ओर बिट में 0. 0. "अंकगणित" शिफ्ट के साथ भरता है, लेफ्ट बिट में मूल मान छोड़ देता है नकारात्मक संख्याओं से निपटने पर अंतर महत्वपूर्ण हो जाता है।)
जब एक अहस्ताक्षरित मूल्य को शिफ्ट किया जाता है, तो C में >> ऑपरेटर एक तार्किक बदलाव है। जब एक हस्ताक्षरित मूल्य को शिफ्ट किया जाता है, तो >> ऑपरेटर एक अंकगणितीय पारी है।
उदाहरण के लिए, 32 बिट मशीन मानकर:
signed int x1 = 5;
assert((x1 >> 1) == 2);
signed int x2 = -5;
assert((x2 >> 1) == -3);
unsigned int x3 = (unsigned int)-5;
assert((x3 >> 1) == 0x7FFFFFFD);
पर विचार करें i
और n
छोड़ दिया और सही ऑपरेंड एक पारी ऑपरेटर की क्रमश; i
पूर्णांक पदोन्नति के बाद, का प्रकार T
। मान लिया n
जाए [0, sizeof(i) * CHAR_BIT)
- अपरिभाषित अन्यथा - हमारे पास ये मामले हैं:
| Direction | Type | Value (i) | Result |
| ---------- | -------- | --------- | ------------------------ |
| Right (>>) | unsigned | ≥ 0 | −∞ ← (i ÷ 2ⁿ) |
| Right | signed | ≥ 0 | −∞ ← (i ÷ 2ⁿ) |
| Right | signed | < 0 | Implementation-defined† |
| Left (<<) | unsigned | ≥ 0 | (i * 2ⁿ) % (T_MAX + 1) |
| Left | signed | ≥ 0 | (i * 2ⁿ) ‡ |
| Left | signed | < 0 | Undefined |
† अधिकांश संकलक इसे अंकगणितीय पारी के रूप में लागू करते हैं implement
यदि परिणाम प्रकार T को ओवरफ्लो करता है तो अपरिभाषित; I का प्रचारित प्रकार
पहले एक गणितीय दृष्टिकोण से तार्किक और अंकगणितीय बदलावों के बीच अंतर है, डेटा प्रकार के आकार के बारे में चिंता किए बिना। लॉजिकल शिफ्ट हमेशा शून्य वाले बिट्स को भरता है जबकि अंकगणितीय शिफ्ट इसे केवल लेफ्ट शिफ्ट के लिए शून्य से भरता है, लेकिन राइट शिफ्ट के लिए यह एमएसबी को कॉपी करता है जिससे ऑपरेंड के संकेत को संरक्षित किया जाता है ( नकारात्मक मूल्यों के लिए दो पूरक पूरक है )।
दूसरे शब्दों में, तार्किक बदलाव शिफ्ट किए गए ऑपरेंड को केवल बिट्स की एक धारा के रूप में देखता है और परिणामी मूल्य के संकेत के बारे में परेशान किए बिना, उन्हें स्थानांतरित करता है। अंकगणित शिफ्ट इसे एक (हस्ताक्षरित) संख्या के रूप में देखता है और संकेत को संरक्षित करता है जैसे कि बदलाव किए जाते हैं।
N द्वारा एक संख्या X की बाईं अंकगणितीय पारी X को 2 n से गुणा करने के बराबर है और इस प्रकार तार्किक बाईं पारी के बराबर है; एक तार्किक बदलाव भी वही परिणाम देगा क्योंकि MSB वैसे भी अंत तक गिर जाता है और संरक्षित करने के लिए कुछ भी नहीं है।
N द्वारा एक नंबर एक्स का एक सही गणित पारी 2 से एक्स के पूर्णांक विभाजन के बराबर है n केवल यदि एक्स गैर नकारात्मक है! पूर्णांक विभाजन कुछ और नहीं बल्कि गणितीय विभाजन और 0 ( ट्रंक ) की ओर गोल है ।
नकारात्मक संख्याओं के लिए, दो के पूरक एन्कोडिंग द्वारा प्रतिनिधित्व किया गया है, n बिट्स द्वारा दाईं ओर स्थानांतरण करने से गणितीय रूप से इसे 2 n से विभाजित करने और −∞ ( मंजिल ) की ओर गोल करने का प्रभाव पड़ता है ; इस प्रकार गैर-नकारात्मक और नकारात्मक मूल्यों के लिए सही स्थानांतरण अलग है।
X n 0, X >> n = X / 2 n = trunc ( X ≥ 2 n ) के लिए
एक्स <0 के लिए, एक्स >> एन = मंजिल (एक्स ) 2 एन )
जहां ÷
गणितीय विभाजन है, /
पूर्णांक विभाजन है। आइए एक उदाहरण देखें:
37) 10 = 100101) 2
37 18 2 = 18.5
37/2 = 18 (18.5 की ओर गोलाई) = 10010) 2 [अंकगणितीय सही पारी का परिणाम]
-37) 10 = 11011011) 2 (एक दो के पूरक पर विचार, 8-बिट प्रतिनिधित्व)
-37 ÷ 2 = -18.5
-३-/ २ = -१-( १ /.५ की ओर गोलाई ०) = १११०११११) २ [अंकगणितीय दाहिने भाग का परिणाम नहीं]
-37 >> 1 = -19 (>> की ओर 18.5 चक्कर) = 11101101) 2 [अंकगणितीय दाएं शिफ्ट का परिणाम]
जैसा कि गाइ स्टील ने बताया , इस विसंगति के कारण एक से अधिक संकलक में कीड़े हो गए हैं । यहां गैर-नकारात्मक (गणित) को अहस्ताक्षरित और हस्ताक्षरित गैर-नकारात्मक मान (C) में मैप किया जा सकता है; दोनों को एक ही माना जाता है और सही-शिफ्टिंग उन्हें पूर्णांक विभाजन द्वारा किया जाता है।
इसलिए तार्किक और अंकगणित बाएं-शिफ्टिंग में बराबर हैं और दाएं स्थानांतरण में गैर-नकारात्मक मूल्यों के लिए; यह नकारात्मक मूल्यों के सही स्थानांतरण में है कि वे भिन्न हैं।
मानक C99 C6.5.7 :
प्रत्येक ऑपरेंड में पूर्णांक प्रकार होंगे।
पूर्णांक प्रोन्नति प्रत्येक ऑपरेंड पर की जाती है। परिणाम का प्रकार पदोन्नत बाएं ऑपरेंड है। यदि सही ऑपरेंड का मान नकारात्मक है या पदोन्नत किए गए बाएं ऑपरेंड की चौड़ाई के बराबर या उससे अधिक है, तो व्यवहार अपरिभाषित है।
short E1 = 1, E2 = 3;
int R = E1 << E2;
उपरोक्त स्निपेट में, दोनों ऑपरेंड बनते हैं int
(पूर्णांक पदोन्नति के कारण); यदि E2
नकारात्मक था या E2 ≥ sizeof(int) * CHAR_BIT
फिर ऑपरेशन अपरिभाषित है। यह इसलिए है क्योंकि उपलब्ध बिट्स से अधिक स्थानांतरण निश्चित रूप से अतिप्रवाह करने वाला है। के R
रूप में घोषित किया गया था short
, int
शिफ्ट ऑपरेशन के परिणाम को संक्षेप में बदल दिया जाएगा short
; एक संकरा रूपांतरण, जिसके परिणामस्वरूप कार्यान्वयन-परिभाषित व्यवहार हो सकता है यदि मूल्य गंतव्य प्रकार में प्रतिनिधित्व करने योग्य नहीं है।
E1 << E2 का परिणाम E1 बाएं-शिफ्ट किया गया E2 बिट स्थिति है; खाली बिट्स शून्य से भरे हुए हैं। यदि E1 का एक अहस्ताक्षरित प्रकार है, तो परिणाम का मान E1 × 2 E2 है , परिणाम प्रकार में अधिकतम प्रतिनिधित्व योग्य मान से एक से कम modulo। यदि E1 में एक हस्ताक्षरित प्रकार और गैर-नकारात्मक मूल्य है, और E1 × 2 E2 परिणाम प्रकार में प्रतिनिधित्व करने योग्य है, तो यह परिणामी मूल्य है; अन्यथा, व्यवहार अपरिभाषित है।
जैसा कि बाईं ओर की शिफ्ट दोनों के लिए समान हैं, खाली बिट्स बस शून्य से भरे हुए हैं। यह बताता है कि दोनों के लिए अहस्ताक्षरित और हस्ताक्षरित प्रकार यह एक अंकगणितीय बदलाव है। मैं इसे अंकगणितीय बदलाव के रूप में व्याख्या कर रहा हूं क्योंकि तार्किक बदलाव बिट्स द्वारा दर्शाए गए मूल्य के बारे में परेशान नहीं करते हैं, यह सिर्फ इसे बिट्स की एक धारा के रूप में देखता है; लेकिन मानक बिट्स के संदर्भ में बात नहीं करता है, लेकिन ई 1 के उत्पाद द्वारा 2 ई 2 के साथ प्राप्त मूल्य के संदर्भ में इसे परिभाषित करके ।
यहाँ पर चेतावनी यह है कि हस्ताक्षरित प्रकारों के लिए मान गैर-ऋणात्मक होना चाहिए और परिणामी प्रकार परिणामी मान होना चाहिए। अन्यथा ऑपरेशन अपरिभाषित है। परिणाम प्रकार अभिन्न पदोन्नति को लागू करने के बाद ई 1 का प्रकार होगा और न कि गंतव्य (चर जो परिणाम को धारण करने वाला है) प्रकार। परिणामी मूल्य अनुमानित रूप से गंतव्य प्रकार में परिवर्तित हो जाता है; यदि यह उस प्रकार का प्रतिनिधित्व करने योग्य नहीं है, तो रूपांतरण कार्यान्वयन-परिभाषित (C99 .36.3.1.3 / 3) है।
यदि ई 1 एक नकारात्मक मूल्य के साथ एक हस्ताक्षरित प्रकार है तो बाएं स्थानांतरण का व्यवहार अपरिभाषित है। यह अपरिभाषित व्यवहार का एक आसान मार्ग है जिसकी आसानी से अनदेखी हो सकती है।
E1 >> E2 का परिणाम E1 सही-स्थानांतरित E2 बिट स्थिति है। यदि E1 में एक अहस्ताक्षरित प्रकार है या यदि E1 में एक हस्ताक्षरित प्रकार और एक गैर-नकारात्मक मूल्य है, तो परिणाम का मान E1 / 2 E2 के भाग का अभिन्न अंग है । यदि ई 1 में एक हस्ताक्षरित प्रकार और एक नकारात्मक मूल्य है, तो परिणामस्वरूप मूल्य कार्यान्वयन-परिभाषित है।
अहस्ताक्षरित और हस्ताक्षरित गैर-नकारात्मक मूल्यों के लिए सही बदलाव बहुत सीधे हैं; खाली बिट्स शून्य से भरे हुए हैं। हस्ताक्षरित नकारात्मक मूल्यों के लिए सही स्थानांतरण का परिणाम कार्यान्वयन-परिभाषित है। उस ने कहा, जीसीसी और विज़ुअल सी ++ जैसे अधिकांश कार्यान्वयन, राइट बिट को संरक्षित करके अंकगणितीय शिफ्टिंग के रूप में राइट-शिफ्टिंग को लागू करते हैं।
जावा के विपरीत, जिसमें >>>
सामान्य से अलग तार्किक शिफ्टिंग के लिए एक विशेष ऑपरेटर होता है >>
और <<
, C और C ++ में केवल कुछ क्षेत्रों के साथ अंकगणित स्थानांतरण होता है, जो अपरिभाषित और कार्यान्वयन-परिभाषित छोड़ दिया जाता है। कारण है कि मैं उन्हें अंकगणित के रूप में डीईएम करता हूं, जो मानक रूप से शिफ्ट किए गए ऑपरेंड को बिट्स की एक धारा के रूप में व्यवहार करने के बजाय ऑपरेशन को खराब करने के कारण है; शायद यही कारण है कि यह उन क्षेत्रों को तार्किक बदलाव के रूप में सभी मामलों को परिभाषित करने के बजाय संयुक्त राष्ट्र / कार्यान्वयन-परिभाषित छोड़ देता है।
-Inf
नकारात्मक और धनात्मक दोनों संख्याओं के लिए सही शिफ्ट राउंड । एक पॉजिटिव नंबर की ओर 0 का चक्कर लगाना एक निजी मामला है -Inf
। ट्रंकटिंग करते समय, आप हमेशा सकारात्मक भारित मान छोड़ते हैं, इसलिए आप अन्यथा सटीक परिणाम से घटाते हैं।
आपको मिलने वाली शिफ्ट के प्रकार के संदर्भ में, महत्वपूर्ण बात उस मूल्य का प्रकार है जिसे आप स्थानांतरित कर रहे हैं। कीड़े का एक क्लासिक स्रोत तब होता है जब आप एक शाब्दिक शिफ्ट कहते हैं, बिट्स से नकाब उतारें। उदाहरण के लिए, यदि आप एक अहस्ताक्षरित पूर्णांक के बाएं-सबसे को गिराना चाहते हैं, तो आप इसे अपने मास्क के रूप में आज़मा सकते हैं:
~0 >> 1
दुर्भाग्य से, यह आपको परेशानी में डालेगा क्योंकि मास्क के सभी बिट्स सेट होंगे क्योंकि मूल्य शिफ्ट किया जा रहा है (~ 0) पर हस्ताक्षर किए गए हैं, इस प्रकार एक अंकगणितीय बदलाव किया जाता है। इसके बजाय, आप मूल्य को अहस्ताक्षरित घोषित करके एक तार्किक बदलाव करना चाहते हैं, जैसे कि कुछ इस तरह से करना:
~0U >> 1;
यहाँ सी में इंट के तार्किक तार्किक बदलाव और अंकगणितीय सही बदलाव की गारंटी के लिए कार्य हैं:
int logicalRightShift(int x, int n) {
return (unsigned)x >> n;
}
int arithmeticRightShift(int x, int n) {
if (x < 0 && n > 0)
return x >> n | ~(~0U >> n);
else
return x >> n;
}
जब आप करते हैं - बाईं ओर 1 से आप 2 से गुणा करते हैं - दाएं बदलाव 1 से आप 2 से विभाजित करते हैं
x = 5
x >> 1
x = 2 ( x=5/2)
x = 5
x << 1
x = 10 (x=5*2)
खैर, मैंने इसे विकिपीडिया पर देखा , और उनका कहना है कि:
C, हालांकि, केवल एक ही सही शिफ्ट ऑपरेटर है, >> कई सी कंपाइलर चुनते हैं कि किस प्रकार के पूर्णांक को किस प्रकार के पूर्णांक के आधार पर स्थानांतरित किया जाना है; अक्सर हस्ताक्षरित पूर्णांकों को अंकगणितीय पारी का उपयोग करके स्थानांतरित किया जाता है, और अहस्ताक्षरित पूर्णांकों को तार्किक बदलाव का उपयोग करके स्थानांतरित किया जाता है।
तो ऐसा लगता है कि यह आपके कंपाइलर पर निर्भर करता है। उस लेख में भी, ध्यान दें कि बाईं पाली अंकगणितीय और तार्किक के लिए समान है। मैं बॉर्डर केस (निश्चित रूप से उच्च बिट सेट) पर कुछ हस्ताक्षरित और अहस्ताक्षरित संख्याओं के साथ एक सरल परीक्षण करने की सलाह दूंगा और देखें कि परिणाम आपके संकलक पर क्या है। मैं इसे एक या दूसरे होने के आधार पर टालने की भी सिफारिश करूंगा क्योंकि ऐसा लगता है कि सी का कोई मानक नहीं है, कम से कम अगर यह उचित है और ऐसी निर्भरता से बचने के लिए संभव है।
बायां शिफ्ट <<
यह किसी भी तरह आसान है और जब भी आप शिफ्ट ऑपरेटर का उपयोग करते हैं, यह हमेशा एक बिट-वार ऑपरेशन होता है, इसलिए हम इसे डबल और फ्लोटिंग ऑपरेशन के साथ उपयोग नहीं कर सकते हैं। जब भी हमने एक शून्य को छोड़ा, इसे हमेशा कम से कम महत्वपूर्ण बिट ( LSB
) में जोड़ा जाता है ।
लेकिन सही पाली में >>
हमें एक अतिरिक्त नियम का पालन करना होता है और उस नियम को "साइन बिट कॉपी" कहा जाता है। "साइन बिट कॉपी" का अर्थ यदि सबसे महत्वपूर्ण बिट है ( MSB
) सेट है, तो एक सही शिफ्ट के बाद फिर से MSB
सेट किया जाएगा यदि इसे रीसेट किया गया था, तो इसे फिर से रीसेट किया जाता है, मतलब अगर पिछले मान शून्य था तो फिर से शिफ्ट करने के बाद, साइन बिट शून्य है यदि पिछली बिट एक थी तो शिफ्ट के बाद यह फिर से एक है। यह नियम बाईं पारी के लिए लागू नहीं है।
राइट शिफ्ट पर सबसे महत्वपूर्ण उदाहरण यदि आप किसी भी नेगेटिव नंबर को राइट शिफ्ट में शिफ्ट करते हैं, तो कुछ शिफ्टिंग के बाद वैल्यू अंत में शून्य तक पहुंच जाती है और फिर इसके बाद अगर इस -1 को किसी भी नंबर पर शिफ्ट किया जाता है तो वैल्यू एक समान रहेगी। कृपया जांचें।
जीसीसी करता है
for -ve -> अंकगणितीय पारी
+ Ve -> तार्किक शिफ्ट के लिए
बहुतों के अनुसार सी compilers:
<<
एक अंकगणित बाईं पारी है या बिटवाइज़ वाम पाली है।>>
एक अंकगणितीय सही शिफ्टर बिटवाइज़ राइट शिफ्ट है।>>
अंकगणित या बिटवाइज़ (तार्किक) है?" आपने उत्तर दिया " >>
अंकगणित या बिटवाइज़।" इस सवाल का जवाब नहीं है।
<<
और >>
ऑपरेटरों तार्किक हैं, अंकगणित नहीं