2 से पूर्णांक संख्या को विभाजित करने के लिए कौन सा बेहतर विकल्प है?


406

निम्नलिखित में से कौन सी तकनीक 2 से पूर्णांक को विभाजित करने के लिए सबसे अच्छा विकल्प है और क्यों?

तकनीक 1:

x = x >> 1;

तकनीक 2:

x = x / 2;

यहाँ xएक पूर्णांक है।


75
यदि आप वास्तव में परिणाम को xफिर से असाइन करना चाहते हैं , तो न तो इस तरह से उपयुक्त है: यह ऑपरेशन के साथ व्यक्त करने के लिए क्या करना है , इसके आधार पर x >>= 1या तो होना चाहिए या x /= 2। इसलिए नहीं कि यह तेज़ है (कोई भी आधुनिक संकलक सभी समरूप, समान, तीव्र असेंबली के सभी समतुल्य को संकलित करेगा) लेकिन क्योंकि यह कम भ्रामक है।
लेफ्टनैबाउट

33
मैं वामपंथ से असहमत हूं। - लेकिन मुझे लगता है कि यह उल्लेखनीय है कि कई प्रोग्रामिंग भाषाओं में अंकगणितीय बदलाव नामक एक ऑपरेशन होता है जो साइन बिट को जगह पर रखता है और हस्ताक्षरित मूल्यों के लिए अपेक्षित रूप से कार्य करता है। वाक्य-विन्यास जैसा हो सकता है x = x >>> 1। यह भी ध्यान दें कि प्लेटफॉर्म और कंपाइलर के आधार पर यह शिफ्ट्स का उपयोग करके डिवीजनों और गुणाओं को मैन्युअल रूप से अनुकूलित करने के लिए काफी उचित हो सकता है। - उदाहरण के लिए, माइक्रो कंट्रोलर की सोच, डब्ल्यू / ओ प्रत्यक्ष गुणन के लिए प्रत्यक्ष ALU समर्थन।
जिम्मीबी

36
मैं पसंद करता हूं x /= 2क्योंकि x >>= 1
मोनाडिक

19
@leftractionabout - मैं x = x / 2इसके बजाय लिखने के लिए बहुत अधिक पठनीय समझ रहा हूँ x /= 2। विषयगत वरीयता शायद :)
जिमीबी

8
@ हन्नोइंदर: निश्चित रूप से व्यक्तिपरक, विशेष रूप से बहुत सारी आदत में। IMO, एक ऐसी भाषा में जहां सभी अंकगणितीय संचालकों का ⬜=संयोजन होता है, जब भी संभव हो इनका उपयोग किया जाना चाहिए। यह तथ्य यह है कि पर शोर और पुट जोर निकाल देता xहै संशोधित है, जबकि सामान्य =ऑपरेटर बल्कि चलता है कि यह एक पुरानी दुनिया की एक पूरी तरह से नए मूल्य स्वतंत्र पर ले जाता है। - हमेशा संयुक्त ऑपरेटरों से परहेज (इतना है कि यह पठनीय है, तो कोई है जो केवल गणितीय ऑपरेटर जानता है) के रूप में अच्छी तरह से अपनी बात हो सकती है, लेकिन फिर आप अत्यंत उपयोगी त्यागना आवश्यकता होगी ++, --, +=, भी।
12:30

जवाबों:


847

उस ऑपरेशन का उपयोग करें जो सबसे अच्छा वर्णन करता है कि आप क्या करने की कोशिश कर रहे हैं।

  • यदि आप संख्या को बिट्स के अनुक्रम के रूप में मान रहे हैं, तो बिटशिफ्ट का उपयोग करें।
  • यदि आप इसे संख्यात्मक मान के रूप में मान रहे हैं, तो विभाजन का उपयोग करें।

ध्यान दें कि वे बिल्कुल समकक्ष नहीं हैं। वे नकारात्मक पूर्णांक के लिए अलग-अलग परिणाम दे सकते हैं। उदाहरण के लिए:

-5 / 2  = -2
-5 >> 1 = -3

(Ideone)


20
मूल प्रश्न 'सर्वश्रेष्ठ' शब्द के बारे में भी अस्पष्ट था। गति, पठनीयता, छात्रों से छल करने के लिए परीक्षा प्रश्न आदि के मामले में 'सर्वश्रेष्ठ' ... 'सबसे अच्छा' का अर्थ क्या है, इसकी व्याख्या के अभाव में, यह सबसे सही उत्तर लगता है।
रे

47
C ++ 03 में, दोनों ही कार्यान्वयन नकारात्मक संख्याओं के लिए परिभाषित किए गए हैं, और समान परिणाम दे सकते हैं। C ++ 11 में, विभाजन को नकारात्मक संख्याओं के लिए अच्छी तरह से परिभाषित किया गया है, लेकिन स्थानांतरण अभी भी कार्यान्वयन परिभाषित है।
जेम्स कान्ज़े

2
जबकि प्रारंभिक सी मानकों में परिभाषित / लागू करने की परिभाषा (नकारात्मक संख्याओं के लिए ऊपर या नीचे गोल है)। यह हमेशा% (modulo / शेष ऑपरेटर) के अनुरूप होना चाहिए।
ctrl-alt-delor

7
"कार्यान्वयन परिभाषित" का अर्थ है कि संकलक के कार्यान्वयनकर्ता को कई कार्यान्वयन विकल्पों के बीच चयन करने के लिए मिला, आमतौर पर पर्याप्त बाधाओं के साथ। इधर, एक बाधा है कि है %और /ऑपरेटरों सकारात्मक और नकारात्मक दोनों ऑपरेंड के लिए लगातार इतना है कि किया जाना चाहिए (a/b)*b+(a%b)==aके संकेत यह बात लागू रहेगी aऔर b। आमतौर पर, लेखक उन विकल्पों का चयन करेगा जो सीपीयू से बाहर सर्वश्रेष्ठ संभव प्रदर्शन प्राप्त करते हैं।
RBerteig

6
तो हर कोई जो कहता है "संकलक इसे किसी भी तरह शिफ्ट में बदल देगा" गलत है, है ना? जब तक संकलक यह गारंटी नहीं दे सकता कि आप एक गैर-नकारात्मक पूर्णांक के साथ काम कर रहे हैं (या तो यह एक स्थिर है या यह एक अहस्ताक्षरित int है), यह इसे एक बदलाव में नहीं बदल सकता है
किप

225

क्या पहले वाले को विभाजित करने जैसा दिखता है? यदि आप विभाजित करना चाहते हैं, तो उपयोग करें x / 2। कंपाइलर यदि संभव हो तो बिट-शिफ्ट का उपयोग करने के लिए इसे अनुकूलित कर सकता है (इसे शक्ति में कमी कहा जाता है), जो इसे बेकार माइक्रो-ऑप्टिमाइज़ेशन बनाता है यदि आप इसे अपने दम पर करते हैं।


15
कई संकलक विभाजन को दो की शक्ति में एक बिटशिफ्ट में नहीं बदलेंगे। यह हस्ताक्षरित पूर्णांकों के लिए एक गलत अनुकूलन होगा। आपको अपने कंपाइलर से असेंबली आउटपुट को देखने और खुद को देखने की कोशिश करनी चाहिए।
exDM69

1
IIRC मैंने इसका उपयोग CUDA पर तेजी से समानांतर कमी करने के लिए किया (पूर्णांक div से बचें)। हालांकि यह एक साल पहले की तुलना में अधिक था, मुझे आश्चर्य है कि आजकल CUDA कंपाइलर कितने स्मार्ट हैं।
नेल्स

9
@ exDM69: कई कंपाइलर हस्ताक्षर किए गए पूर्णांक के लिए भी ऐसा करेंगे, और केवल हस्ताक्षर के अनुसार उन्हें समायोजित करेंगे। : इन बातों के साथ खेलने के लिए एक अच्छा उपकरण है tinyurl.com/6uww253
PlasmaHH

19
@ exDM69: और यह प्रासंगिक है, कैसे? मैंने कहा "यदि संभव हो", "हमेशा" नहीं। यदि अनुकूलन गलत है, तो इसे मैन्युअल रूप से करना सही नहीं है (जैसा कि उल्लेख किया गया है, जीसीसी हस्ताक्षरित पूर्णांक के लिए उचित प्रतिस्थापन का पता लगाने के लिए पर्याप्त स्मार्ट है)।
बिल्ली प्लस प्लस

4
विकीपीडिया पेज को देखते हुए, यह स्पष्ट रूप से विवादास्पद है, लेकिन मैं इसे एक ताकत में कमी नहीं कहूंगा। एक ताकत में कमी तब होती है, जब आप एक लूप में होते हैं, उदाहरण के लिए, लूप में पिछले मान जोड़कर, उदाहरण के लिए, गुणा को घटाते हैं। यह एक peephole अनुकूलन के अधिक है, जो संकलक बहुत मज़बूती से कर सकते हैं।
SomeCallMeTim

189

पर ढेर करने के लिए: का उपयोग करने के पक्ष में कई कारण x = x / 2; हैं यहां कुछ हैं:

  • यह आपके इरादे को और अधिक स्पष्ट रूप से व्यक्त करता है (यह मानते हुए कि आप बिट ट्विडलिंग रजिस्टर बिट्स या कुछ के साथ काम नहीं कर रहे हैं)

  • कंपाइलर इसे शिफ्ट ऑपरेशन में वैसे भी कम कर देगा

  • यहां तक ​​कि अगर कंपाइलर ने इसे कम नहीं किया और शिफ्ट की तुलना में धीमी गति से संचालन को चुना, तो संभावना यह है कि यह आपके कार्यक्रम के प्रदर्शन को मापने योग्य तरीके से प्रभावित करता है, स्वयं ही गायब हो जाता है (और यदि यह इसे औसत रूप से प्रभावित करता है, तो आपके पास वास्तविक है एक पारी का उपयोग करने का कारण)

  • यदि विभाजन एक बड़ी अभिव्यक्ति का हिस्सा बनने जा रहा है, तो यदि आप डिवीजन ऑपरेटर का उपयोग करते हैं, तो आपको पूर्ववर्ती अधिकार प्राप्त होने की अधिक संभावना है:

    x = x / 2 + 5;
    x = x >> 1 + 5;  // not the same as above
  • हस्ताक्षरित अंकगणित ऊपर बताई गई पूर्ववर्ती समस्या से भी अधिक जटिल हो सकती है

  • पुनरावृत्ति करने के लिए - संकलक पहले से ही आपके लिए वैसे भी ऐसा करेगा। वास्तव में, यह केवल दो की शक्तियों की नहीं बल्कि सभी प्रकार की संख्याओं के लिए बदलावों को जोड़ता है, जोड़ता है, और गुणा करता है। इस बारे में अधिक जानकारी के लिए लिंक के लिए यह प्रश्न देखें ।

संक्षेप में, आप एक पाली को कोड करके कुछ भी नहीं खरीदते हैं जब आप वास्तव में गुणा या विभाजित करने का मतलब रखते हैं, सिवाय एक बग को पेश करने की वृद्धि की संभावना के अलावा। यह एक जीवन भर रहा है क्योंकि उपयुक्त होने पर कंपाइलर्स इस तरह की चीज़ को एक पाली में अनुकूलित करने के लिए पर्याप्त स्मार्ट नहीं थे।


5
यह भी जोड़ने योग्य है कि जहां पूर्ववर्तीता के नियम हैं, वहीं कोष्ठक का उपयोग करने में कोई गलत बात नहीं है। कुछ उत्पादन कोड को सुधारने के दौरान, मैंने वास्तव में कहीं अधिक पठनीय के बजाय फॉर्म के कुछ a/b/c*d(जहां a..dसांख्यिक चर को निरूपित किया) देखा (a*d)/(b*c)

1
प्रदर्शन और अनुकूलन कंपाइलर और लक्ष्य पर निर्भर करते हैं। उदाहरण के लिए, मैं एक माइक्रोकंट्रोलर के लिए कुछ काम करता हूं जहां -O0 से अधिक कुछ भी अक्षम होता है जब तक कि आप वाणिज्यिक संकलक नहीं खरीदते हैं, इसलिए संकलक निश्चित रूप से बिटशिफ्ट्स में विभाजित नहीं होगा। इसके अलावा, बिटशिफ्ट एक चक्र लेते हैं और इस लक्ष्य पर 18 चक्र लेते हैं और चूंकि माइक्रोकंट्रोलर घड़ी की गति बहुत कम है, यह वास्तव में एक ध्यान देने योग्य प्रदर्शन हिट हो सकता है (लेकिन यह आपके कोड पर निर्भर करता है - आपको निश्चित रूप से उपयोग करना चाहिए / प्रोफाइलिंग आपको बताता है। इसकी एक समस्या!)

4
@JackManey, अगर वहाँ कोई संभावना है कि a*dया b*cएक अतिप्रवाह का उत्पादन होगा, कम पठनीय रूप के बराबर नहीं है और एक स्पष्ट लाभ है। पुनश्च मैं सहमत हूँ कि कोष्ठक तुम्हारा सबसे अच्छा दोस्त है।
मार्क रैनसम

@MarkRansom - एक उचित बिंदु (भले ही मैं a/b/c*dआर कोड में भाग गया - एक संदर्भ में जहां अतिप्रवाह का मतलब होगा कि डेटा के साथ कुछ गंभीर रूप से गलत था - और सी कोड के एक प्रदर्शन-महत्वपूर्ण ब्लॉक में नहीं)।

कोड x=x/2;केवल "क्लियर" है, x>>=1यदि xकभी भी एक विषम ऋणात्मक संख्या नहीं होगी या किसी को ऑफ-बाय-वन त्रुटियों की परवाह नहीं है। अन्यथा x=x/2;और x>>=1;अलग-अलग अर्थ हैं। क्या एक की जरूरत के आधार पर परिकलित मूल्य है x>>=1, मैं से स्पष्ट रूप में है कि सम्मान दिया है x = (x & ~1)/2या x = (x < 0) ? (x-1)/2 : x/2, या किसी अन्य सूत्रीकरण मैं दो से विभाजन का उपयोग कर के बारे में सोच सकते हैं। इसी तरह अगर किसी को उसके द्वारा गणना किए गए मूल्य की आवश्यकता होती है x/=2, तो वह स्पष्ट है ((x + ((unsigned)x>>31)>>1)
सुपरकैट

62

सबसे अच्छा विकल्प कौन सा है और पूर्णांक संख्या को 2 से विभाजित करने के लिए क्यों?

निर्भर करता है कि आपको सबसे अच्छे से क्या मतलब है ।

यदि आप चाहते हैं कि आपके सहकर्मी आपसे घृणा करें, या अपने कोड को पढ़ना मुश्किल बना दें, तो मैं निश्चित रूप से पहले विकल्प के साथ जाऊंगा।

यदि आप एक संख्या को 2 से विभाजित करना चाहते हैं, तो दूसरे के साथ जाएं।

दोनों समान नहीं हैं, वे समान व्यवहार नहीं करते हैं यदि संख्या नकारात्मक है या बड़े भावों के अंदर है - बिटशिफ्ट में पूर्ववर्ती तुलना में कम है, +या -विभाजन में उच्च पूर्वता है।

इसका आशय क्या है, यह व्यक्त करने के लिए आपको अपना कोड लिखना चाहिए। यदि प्रदर्शन आपकी चिंता है, तो चिंता न करें, ऑप्टिमाइज़र इन प्रकार के सूक्ष्म-अनुकूलन पर एक अच्छा काम करता है।


58

बस डिवाइड ( /) का उपयोग करें , यह स्पष्ट है। संकलक तदनुसार अनुकूलित करेगा।


34
कंपाइलर को अपने हिसाब से ऑप्टिमाइज़ करना चाहिए
नोक्टिस स्काईटॉवर

12
यदि संकलक तदनुसार अनुकूलन नहीं करता है, तो आपको एक बेहतर संकलक का उपयोग करना चाहिए
डेविड स्टोन

3
@DavidStone: पर क्या प्रोसेसर कर सकते हैं किसी भी 1 के अलावा अन्य निरंतर द्वारा एक संभवतः नकारात्मक हस्ताक्षर किए पूर्णांक के एक संकलक का अनुकूलन विभाजन एक बदलाव के रूप में कुशल के रूप में होना करने के लिए?
सुपरकैट

1
@ सुपरकैट: यह एक अच्छा बिंदु है। आप निश्चित रूप से मान को एक अहस्ताक्षरित पूर्णांक में संग्रहीत कर सकते हैं (जो मुझे लगता है कि हस्ताक्षर / अहस्ताक्षरित बेमेल चेतावनियों के साथ संयुक्त होने पर उनकी तुलना में बहुत खराब प्रतिष्ठा है), और अधिकांश संकलक के पास यह कहने का एक तरीका भी है कि वे कुछ इस तरह से सही हैं: । मैं एक अनुकूलता मैक्रो में लपेटना पसंद करूंगा और इसमें कुछ ऐसा ASSUME(x >= 0); x /= 2;होगा x >>= 1;, लेकिन यह अभी भी एक महत्वपूर्ण बिंदु है।
डेविड स्टोन

39

मैं अन्य उत्तरों से सहमत हूं कि आपको पक्ष लेना चाहिए x / 2क्योंकि इसका इरादा स्पष्ट है, और संकलक को आपके लिए इसे अनुकूलित करना चाहिए।

हालाँकि, x / 2अधिक तरजीह देने का दूसरा कारण x >> 1यह है कि >>यदि xकोई हस्ताक्षर किया गया है intऔर नकारात्मक है तो कार्यान्वयन-निर्भरता का व्यवहार है।

खंड 6.5.7 से, आईएसओ C99 मानक की बुलेट 5:

का परिणाम E1 >> E2है E1सही-स्थानांतरित E2बिट पदों। यदि E1एक अहस्ताक्षरित प्रकार है या यदि E1एक हस्ताक्षरित प्रकार और एक नॉनगेटिव मूल्य है, तो परिणाम का मूल्य E1/ 2 के भागफल का अभिन्न अंग है E2। यदि E1एक हस्ताक्षरित प्रकार और एक नकारात्मक मूल्य है, तो परिणामी मूल्य कार्यान्वयन-परिभाषित है।


3
यह ध्यान देने योग्य है कि x>>scalepowerनकारात्मक संख्याओं के लिए कई कार्यान्वयन जो व्यवहार को परिभाषित करते हैं, वह ठीक वही होगा जो स्क्रीन रेंडरिंग जैसे उद्देश्यों के लिए दो की शक्ति से एक मूल्य को विभाजित करते समय आवश्यक है, जब x/scalefactorतक कि गलत मानों को सुधार लागू नहीं किया जाता है।
सुपरकैट

32

x / 2स्पष्ट है, और x >> 1बहुत तेज नहीं है (एक माइक्रो-बेंचमार्क के अनुसार, जावा जेवीएम के लिए लगभग 30% तेज)। जैसा कि अन्य ने नोट किया है, नकारात्मक संख्याओं के लिए गोलाई थोड़ा अलग है, इसलिए आपको इस पर विचार करना होगा जब आप नकारात्मक संख्याओं को संसाधित करना चाहते हैं। कुछ compilers स्वचालित रूप से परिवर्तित कर सकते हैं x / 2करने के लिए x >> 1करता है, तो वे जानते हैं कि संख्या नकारात्मक नहीं हो सकता (यहां तक कि सोचा था कि मैं इस की पुष्टि नहीं कर सकता है)।

यहां तक ​​कि x / 2(धीमी) डिवीजन सीपीयू अनुदेश का उपयोग नहीं किया जा सकता है, क्योंकि कुछ शॉर्टकट संभव हैं , लेकिन यह अभी भी धीमी है x >> 1

(यह एक सी / C ++ है सवाल है, अन्य प्रोग्रामिंग भाषाओं अधिक ऑपरेटरों की है। जावा के लिए वहाँ भी है अहस्ताक्षरित सही पारी, x >>> 1जो फिर से अलग है। यह इतना है कि, सही ढंग से दो मानों का मतलब (औसत) मूल्य की गणना करने की अनुमति देता है (a + b) >>> 1इच्छा के बहुत बड़े मूल्यों के लिए भी मतलब मान aऔर b। सरणी सूचकांक बहुत बड़ी प्राप्त कर सकते हैं, तो यह द्विआधारी खोज के लिए उदाहरण के लिए आवश्यक है। हुई द्विआधारी खोज के कई संस्करणों में एक बग है, क्योंकि वे प्रयोग किया जाता (a + b) / 2औसत की गणना करने के लिए। यह नहीं करता है 't सही ढंग से काम करता है। सही समाधान (a + b) >>> 1इसके बजाय उपयोग करना है।)


1
संकलनकर्ता परिवर्तित नहीं कर सकते x/2करने के लिए x>>1ऐसे मामलों में जहां में xनकारात्मक हो सकता है। यदि कोई चाहता है तो वह मूल्य है जो x>>1गणना करेगा, यह निश्चित रूप से किसी भी x/2मूल्य को शामिल करने वाले किसी भी अभिव्यक्ति की तुलना में तेजी से होगा ।
सुपरकैट

तुम सही हो। एक कंपाइलर केवल तभी परिवर्तित हो सकता x/2है x>>1जब वह जानता है कि मूल्य नकारात्मक नहीं है। मैं अपने उत्तर को अपडेट करने की कोशिश करूंगा।
थॉमस म्यूलर

compilers अभी भी एक से बचने है divअनुदेश हालांकि, परिवर्तित करके x/2में (x + (x<0?1:0)) >> 1(जहां >>, कि संकेत बिट्स में बदलाव अंकगणितीय सही-शिफ्ट है)। यह 4 निर्देश लेता है: मान की प्रतिलिपि करें, shr (एक reg में सिर्फ साइन बिट प्राप्त करने के लिए), जोड़ें, सर। goo.gl/4F8Ms4
पीटर कॉर्ड्स

प्रश्न को C और C ++ के रूप में टैग किया गया है।
जोश सैनफोर्ड

22

नुथ ने कहा:

सभी बुराईयो की जड़ समयपूर्व इष्टतमीकरण है।

इसलिए मैं उपयोग करने का सुझाव देता हूं x /= 2;

इस तरह से कोड को समझना आसान है और मुझे यह भी लगता है कि उस रूप में इस ऑपरेशन का अनुकूलन, प्रोसेसर के लिए एक बड़ा अंतर नहीं है।


4
दो की शक्ति से संख्या को कम करने की पसंदीदा विधि पर आप क्या विचार करेंगे यदि कोई पूर्णांक को स्वयंसिद्ध (जो कि प्राकृतिक संख्या और वास्तविक संख्या पर लागू होता है) को रोकना चाहता है (n + d) / d = (n / d) + 1? जब स्केलिंग ग्राफिक्स के परिणाम में दृश्यमान "सीम" दिखाई देगा, तो उल्लंघन। अगर कोई ऐसी चीज चाहता है जो एक समान हो और लगभग शून्य के बारे में सममित हो, (n+8)>>4अच्छी तरह से काम करती है। क्या आप कोई भी दृष्टिकोण प्रस्तुत कर सकते हैं जो बिना हस्ताक्षरित सही बदलाव का उपयोग किए बिना स्पष्ट या कुशल है?
सुपरकैट

19

निर्णय लेने में मदद करने के लिए संकलक आउटपुट पर एक नज़र डालें। मैंने यह परीक्षण x86-64 पर
gcc (GCC) 4.2.1 20070719 [FreeBSD] के साथ चलाया

इसके अलावा संकलक आउटपुट को गॉडबोल्ट पर ऑनलाइन देखें ।

आप जो देख रहे हैं वह यह है कि कंपाइलर sarlदोनों मामलों में एक (अंकगणितीय राइट-शिफ्ट) निर्देश का उपयोग करता है , इसलिए यह दो अभिव्यक्तियों के बीच समानता को पहचानता है। यदि आप डिवाइड का उपयोग करते हैं, तो संकलक को भी नकारात्मक संख्याओं के लिए समायोजित करने की आवश्यकता है। ऐसा करने के लिए यह साइन बिट को सबसे कम ऑर्डर बिट पर शिफ्ट करता है, और परिणाम में इसे जोड़ता है। यह नकारात्मक संख्या को स्थानांतरित करने के दौरान ऑफ-बाय-वन समस्या को ठीक करता है, जबकि एक विभाजन क्या करता है।
चूंकि विभाजन का मामला 2 बदलाव करता है, जबकि स्पष्ट बदलाव का मामला केवल एक ही करता है, अब हम यहां कुछ जवाबों द्वारा मापा गया कुछ प्रदर्शन अंतर बता सकते हैं।

विधानसभा उत्पादन के साथ सी कोड:

विभाजन के लिए, आपका इनपुट होगा

int div2signed(int a) {
  return a / 2;
}

और यह संकलन करता है

    movl    %edi, %eax
    shrl    $31, %eax
    addl    %edi, %eax
    sarl    %eax
    ret

इसी तरह पारी के लिए

int shr2signed(int a) {
  return a >> 1;
}

आउटपुट के साथ:

    sarl    %edi
    movl    %edi, %eax
    ret

जो कुछ कर रहा है, उसके आधार पर, यह ऑफ-बाय-वन एरर को ठीक कर सकता है, या इससे ऑफ-बाय-वन एरर हो सकता है (इसकी तुलना में वास्तव में जरूरत है), जिसे ठीक करने के लिए आगे कोड के उपयोग की आवश्यकता होगी। यदि कोई चाहता है कि एक परिणाम प्राप्त हो, एक सही बदलाव किसी भी वैकल्पिक विकल्प से अधिक तेज और आसान है, जिसे मैं जानता हूं।
सुपरकैट

यदि आपको एक मंजिल की आवश्यकता है, तो यह संभावना नहीं है कि आप वर्णन करेंगे कि आप "2 से विभाजित" के रूप में क्या चाहते हैं
माइकल डोनोह्यू

दोनों प्राकृतिक संख्याओं और वास्तविक संख्याओं का विभाजन स्वयंसिद्ध को जोड़ता है जो (n + d) / d = (n / d) +1 है। वास्तविक संख्याओं का विभाजन भी (-n) / d = - (n / d), एक स्वयंसिद्ध है जो प्राकृतिक संख्याओं के साथ अर्थहीन है। एक डिवीजन ऑपरेटर होना संभव नहीं है जो पूर्णांकों पर बंद है और दोनों एक्सिओम्स को बढ़ाता है। मेरे दिमाग में, यह कहते हुए कि पहला स्वयंसिद्ध सभी नंबरों के लिए होना चाहिए और दूसरा केवल वास्तविक के लिए यह कहकर अधिक स्वाभाविक लगता है कि पहले पूरे नंबरों या रैलियों के लिए होना चाहिए, लेकिन पूर्णांक के लिए नहीं। इसके अलावा, मैं उत्सुक हूं कि किन मामलों में दूसरा स्वयंसिद्ध वास्तव में उपयोगी है
सुपरकाट

1
एक पूर्णांक विभाजन विधि जो पहले स्वयंसिद्ध को संतुष्ट करती है वह संख्या रेखा को आकार के क्षेत्रों में विभाजित करेगी d। ऐसा विभाजन कई उद्देश्यों के लिए उपयोगी है। यहां तक ​​कि अगर किसी को 0 और -1 के अलावा कहीं और ब्रेकपॉइंट होगा, तो एक ऑफसेट को जोड़ने से यह स्थानांतरित हो जाएगा। एक पूर्णांक विभाजन जो दूसरे स्वयंसिद्ध को संतुष्ट करता है वह संख्या रेखा को उन क्षेत्रों में विभाजित करेगा जो अधिकतर आकार के होते हैं d, लेकिन इनमें से एक आकार का होता है 2*d-1। बिल्कुल "बराबर" विभाजन नहीं। क्या आप सुझाव दे सकते हैं कि ऑडबॉल विभाजन वास्तव में उपयोगी कब है?
सुपरकैट

Shr2sign के लिए आपका कंपाइलर आउटपुट गलत है। x86 पर gcc अंकगणितीय पारियों ( sar) के साथ हस्ताक्षरित पूर्णांक के >> को लागू करने का विकल्प चुनता है । goo.gl/KRgIkb । यह मेलिंग सूची पोस्ट ( gcc.gnu.org/ml/gcc/2000-04/msg00152.html ) पुष्टि करती है कि gcc ऐतिहासिक रूप से हस्ताक्षरित किलों के लिए अंकगणितीय पारियों का उपयोग करता है, इसलिए यह अत्यधिक संभावना नहीं है कि FreeBSD Fcc 4.2.1 अहस्ताक्षरित पारी का उपयोग किया हो। मैंने आपकी पोस्ट को ठीक करने के लिए अपडेट किया और शुरुआती पैराग्राफ में कहा गया कि दोनों इस्तेमाल किए गए श्रग, जब यह वास्तव में एसएआर है कि वे दोनों का उपयोग करते हैं। SHR यह है कि यह /केस के लिए साइन बिट कैसे निकालता है । इसमें एक गॉडबॉल्ट लिंक भी शामिल है।
पीटर कॉर्ड्स

15

बस एक जोड़ा नोट -

x * = 0.5 अक्सर वीएम आधारित कुछ भाषाओं में तेजी से होगा - विशेष रूप से क्रियात्मक, क्योंकि चर को 0 से विभाजित करने के लिए जाँच नहीं करनी होगी।


2
@ मिनिटेक: यह इतनी बुरी परीक्षा है। परीक्षण में सभी कोड स्थिर है। इससे पहले कि कोड भी JITed हो, यह सभी स्थिरांक को समाप्त कर देगा।

@ M28: मुझे पूरा यकीन था कि jsPerf के इंटर्ल्स (यानी eval) ने हर बार नया काम किया है। भले ही, हाँ, यह एक बहुत बुरा परीक्षण है, क्योंकि यह एक बहुत ही मूर्खतापूर्ण अनुकूलन है।
Ry-

13

x = x / 2; OR का उपयोग करें x /= 2;क्योंकि यह संभव है कि भविष्य में एक नया प्रोग्रामर इस पर काम करे। इसलिए उसके लिए यह पता लगाना आसान हो जाएगा कि कोड की लाइन में क्या चल रहा है। हर कोई इस तरह के अनुकूलन के बारे में पता नहीं हो सकता है।


12

मैं प्रोग्रामिंग प्रतियोगिताओं के उद्देश्य के लिए कह रहा हूं। आम तौर पर उनके पास बहुत बड़े इनपुट होते हैं जहां 2 से विभाजन कई बार होता है और यह ज्ञात है कि इनपुट सकारात्मक या नकारात्मक है।

x >> 1 x / 2 से बेहतर होगा। मैंने ideone.com पर एक प्रोग्राम चलाकर जाँच की जहाँ 2 ऑपरेशनों के 10 ^ 10 से अधिक विभाजन हुए। x / 2 ने लगभग 5.5 का समय लिया जबकि x >> 1 ने समान कार्यक्रम के लिए लगभग 2.6 लिया।


1
अहस्ताक्षरित मूल्यों के लिए, एक संकलक का अनुकूलन करना चाहिए x/2करने के लिए x>>1। हस्ताक्षरित मूल्यों के लिए, लगभग सभी कार्यान्वयन x>>1एक अर्थ के लिए परिभाषित करते हैं, जो समान है, x/2लेकिन xसकारात्मक होने पर तेजी से गणना की जा सकती है, और नकारात्मक x/2होने पर उपयोगी रूप से अलग xहै।
सुपरकाट

12

मैं कहूंगा कि विचार करने के लिए कई चीजें हैं।

  1. बिटशिफ्ट तेज होना चाहिए, क्योंकि बिट्स को स्थानांतरित करने के लिए वास्तव में कोई विशेष गणना की आवश्यकता नहीं है, हालांकि जैसा कि बताया गया है, नकारात्मक संख्याओं के साथ संभावित मुद्दे हैं। यदि आपके पास सकारात्मक संख्या होना सुनिश्चित है, और गति की तलाश कर रहे हैं तो मैं बिटशिफ्ट की सिफारिश करूंगा।

  2. मनुष्य को पढ़ने के लिए डिवीजन ऑपरेटर बहुत आसान है। इसलिए यदि आप कोड पठनीयता की तलाश में हैं, तो आप इसका उपयोग कर सकते हैं। ध्यान दें कि कंपाइलर ऑप्टिमाइज़ेशन का क्षेत्र बहुत आगे बढ़ चुका है, इसलिए कोड को पढ़ना और समझना आसान है और यह अच्छा अभ्यास है।

  3. अंतर्निहित हार्डवेयर के आधार पर, संचालन में अलग गति हो सकती है। आमलोगों का कानून आम मामले को तेजी से आगे बढ़ाने का है। तो आपके पास हार्डवेयर हो सकता है जो दूसरों की तुलना में अलग-अलग ऑपरेशन कर सकता है। उदाहरण के लिए, 0.5 से गुणा करना 2. से विभाजित होने से अधिक तेज हो सकता है (यदि आप पूर्णांक विभाजन को लागू करना चाहते हैं तो आपको गुणा का तल लेने की आवश्यकता हो सकती है)।

यदि आप शुद्ध प्रदर्शन के बाद हैं, तो मैं कुछ परीक्षण बनाने की सलाह दूंगा जो लाखों बार संचालन कर सकते हैं। यह निर्धारित करने के लिए कि आपके ओएस / हार्डवेयर / कम्पाइलर / कोड के साथ सांख्यिकीय रूप से सबसे अच्छा कौन सा है, निष्पादन को कई बार निष्पादित करें (आपका नमूना आकार)।


2
"बिटशिफ्ट तेज होना चाहिए"। कंपाइलर्स बिटशिफ्ट्स में डिवीजनों को ऑप्टिमाइज़ करेंगे
ट्रेवर हिक्की

मुझे आशा है कि वे करेंगे, लेकिन जब तक आपके पास संकलक के स्रोत तक पहुंच नहीं होगी, आप सुनिश्चित नहीं हो सकते :)
जेम्स ओरेवेक

1
मैं भी बिटशिफ्ट की सिफारिश करूंगा यदि किसी का कार्यान्वयन इसे सबसे आम फैशन में संभालता है और जिस तरह से नकारात्मक संख्याओं को संभालना चाहता है >>वह क्या करता है और क्या नहीं करता है के साथ मेल खाता /है।
सुपरकैट

12

जहां तक ​​सीपीयू का सवाल है, बिट-शिफ्ट ऑपरेशन डिवीजन ऑपरेशंस की तुलना में तेज होते हैं। हालांकि, कंपाइलर यह जानता है और उचित रूप से उस सीमा तक अनुकूलन करेगा, जो आप कर सकते हैं, इसलिए आप उस तरीके से कोड कर सकते हैं जो सबसे अधिक समझ में आता है और यह जानना आसान है कि आपका कोड कुशलता से चल रहा है। लेकिन याद रखें कि एक unsigned intकैन (कुछ मामलों में) intपहले बताए गए कारणों से बेहतर हो सकता है। यदि आपको हस्ताक्षरित अंकगणित की आवश्यकता नहीं है, तो साइन बिट को शामिल न करें।


11

x = x / 2; उपयोग करने के लिए उपयुक्त कोड है .. लेकिन एक ऑपरेशन आपके स्वयं के कार्यक्रम पर निर्भर करता है कि आप उत्पादन कैसे करना चाहते थे।


11

अपने इरादे स्पष्ट करें ... उदाहरण के लिए, यदि आप विभाजित करना चाहते हैं, तो x / 2 का उपयोग करें, और संकलक को इसे शिफ्ट ऑपरेटर (या कुछ और) में बदलने दें।

आज के प्रोसेसर इन अनुकूलन को आपके कार्यक्रमों के प्रदर्शन पर कोई प्रभाव नहीं पड़ने देंगे।


10

इसका उत्तर उस वातावरण पर निर्भर करेगा, जिसके तहत आप काम कर रहे हैं।

  • यदि आप 8-बिट माइक्रोकंट्रोलर पर काम कर रहे हैं या गुणन के लिए हार्डवेयर समर्थन के बिना कुछ भी, बिट शिफ्टिंग की उम्मीद है और सामान्य है, और जबकि कंपाइलर लगभग निश्चित रूप से बदल x /= 2जाएगा x >>= 1, एक विभाजन प्रतीक की उपस्थिति उस वातावरण में अधिक भौहें बढ़ाएगी। किसी विभाजन को प्रभावित करने के लिए एक बदलाव का उपयोग करना।
  • यदि आप प्रदर्शन-महत्वपूर्ण वातावरण या कोड के अनुभाग में काम कर रहे हैं, या आपका कोड संकलक अनुकूलन के साथ संकलित किया जा सकता है, तो x >>= 1इसका तर्क समझाने वाली टिप्पणी संभवतः उद्देश्य की स्पष्टता के लिए सबसे अच्छा है।
  • यदि आप उपरोक्त शर्तों में से एक के तहत नहीं हैं, तो बस का उपयोग करके अपने कोड को अधिक पठनीय बनाएं x /= 2। अगले प्रोग्रामर को बचाने के लिए बेहतर है जो आपके कोड को देखने के लिए आपके शिफ्ट ऑपरेशन पर 10 सेकंड के डबल-टेक को अनावश्यक रूप से साबित करता है कि आपको पता था कि शिफ्ट अधिक कुशल सैंस कंपाइलर अनुकूलन था।

ये सभी अहस्ताक्षरित पूर्णांक मानते हैं। सरल पारी शायद वह नहीं है जिसे आप हस्ताक्षरित करना चाहते हैं। इसके अलावा, डैनियल x *= 0.5एक्शनस्क्रिप्ट जैसी कुछ भाषाओं के लिए उपयोग करने के बारे में एक अच्छी बात पेश करता है ।


8

मॉड 2, के लिए परीक्षण = 1. सी में सिंटैक्स dunno। लेकिन यह सबसे तेज़ हो सकता है।


7

सही शिफ्ट को विभाजित करता है:

q = i >> n; is the same as: q = i / 2**n;

यह कभी-कभी स्पष्टता की कीमत पर कार्यक्रमों को गति देने के लिए उपयोग किया जाता है। मुझे नहीं लगता कि आपको यह करना चाहिए। कंपाइलर स्मार्टअप को स्वचालित रूप से करने के लिए पर्याप्त स्मार्ट है। इसका मतलब यह है कि शिफ्ट में डालने से आपको स्पष्टता की कीमत पर कुछ नहीं मिलता है

प्रैक्टिकल C ++ प्रोग्रामिंग के इस पेज पर एक नज़र डालें


यदि कोई मान की गणना करना चाहता है, जैसे कि अधिकतम के पास नहीं के (x+128)>>8मूल्यों के लिए गणना करेगा x, तो कोई इसे शिफ्ट के बिना कैसे कर सकता है? ध्यान दें कि (x+128)/256काम नहीं करेगा। क्या आप किसी अच्छी अभिव्यक्ति के बारे में जानते हैं?
सुपरकैट

7

जाहिर है, यदि आप अपना कोड अगले आदमी के लिए लिख रहे हैं जो इसे पढ़ता है, तो "x / 2" की स्पष्टता के लिए जाएं।

हालाँकि, यदि गति आपका लक्ष्य है, तो इसे परिणामों और समय दोनों तरीकों से आज़माएँ।कुछ महीने पहले मैंने एक बिटमैप कन्वेंशन रूटीन पर काम किया था जिसमें पूर्णांक की एक सरणी के माध्यम से कदम रखना और प्रत्येक तत्व को 2 से विभाजित करना था। मैंने इसे अनुकूलित करने के लिए "x >> 1" को बदलने की पुरानी चाल सहित सभी प्रकार की चीजों को किया "x" / 2 "।

जब मैंने वास्तव में दोनों तरीकों से मुझे आश्चर्यचकित किया तो मुझे आश्चर्य हुआ कि x / 2 x >> 1 से अधिक तेज था

यह Microsoft VS2008 C ++ का उपयोग डिफ़ॉल्ट अनुकूलन के साथ चालू हुआ था।


4

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

जैसा कि देखो और महसूस करो। इंजीनियरों के रूप में हम कब सौंदर्य प्रसाधनों से इतने जुड़ गए कि सुंदर महिलाएं भी इसका इस्तेमाल नहीं करतीं! :)


3

X / Y एक सही है ... और ">>" शिफ्टिंग ऑपरेटर..यदि हम दो पूर्णांकों को विभाजित करना चाहते हैं जो हम (/) लाभांश ऑपरेटर का उपयोग कर सकते हैं। बिट्स को शिफ्ट करने के लिए शिफ्ट ऑपरेटर का उपयोग किया जाता है।

एक्स = एक्स / 2; एक्स / = 2; हम इस तरह का उपयोग कर सकते हैं ..


0

जबकि x >> 1 x / 2 से तेज है, >> नकारात्मक मूल्यों से निपटने के दौरान >> का उचित उपयोग थोड़ा अधिक जटिल है। यह निम्नलिखित के समान कुछ की आवश्यकता है:

// Extension Method
public static class Global {
    public static int ShiftDivBy2(this int x) {
        return (x < 0 ? x + 1 : x) >> 1;
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.