पूर्णांक विभाजन को लागू करने में जीसीसी एक अजीब संख्या से गुणा का उपयोग क्यों करता है?


227

के बारे में पढ़ रहा हूँ div और mulविधानसभा संचालन के रहा हूं, और मैंने सी में एक साधारण कार्यक्रम लिखकर उन्हें कार्रवाई में देखने का फैसला किया है:

फ़ाइल विभाजन

#include <stdlib.h>
#include <stdio.h>

int main()
{
    size_t i = 9;
    size_t j = i / 5;
    printf("%zu\n",j);
    return 0;
}

और उसके बाद विधानसभा भाषा कोड बनाना:

gcc -S division.c -O0 -masm=intel

लेकिन जनरेट की गई division.sफ़ाइल को देखते हुए, इसमें कोई भी div ऑपरेशंस नहीं है! इसके बजाय, यह किसी तरह का काला जादू करता है जिसमें बिट शिफ्टिंग और मैजिक नंबर होते हैं। यहाँ एक कोड स्निपेट है जो गणना करता है i/5:

mov     rax, QWORD PTR [rbp-16]   ; Move i (=9) to RAX
movabs  rdx, -3689348814741910323 ; Move some magic number to RDX (?)
mul     rdx                       ; Multiply 9 by magic number
mov     rax, rdx                  ; Take only the upper 64 bits of the result
shr     rax, 2                    ; Shift these bits 2 places to the right (?)
mov     QWORD PTR [rbp-8], rax    ; Magically, RAX contains 9/5=1 now, 
                                  ; so we can assign it to j

यहाँ क्या चल रहा है? GCC सभी में div का उपयोग क्यों नहीं करता है? यह मैजिक नंबर कैसे उत्पन्न करता है और सब कुछ काम क्यों करता है?


29
जीसीसी स्थिरांक द्वारा विभाजनों का अनुकूलन करता है, 2,3,4,5,6,7,8 द्वारा विभाजनों का प्रयास करें और आप प्रत्येक मामले में सबसे अलग कोड देखेंगे।
Jabberwocky

28
नोट: मैजिक नंबर एक या सिर्फ (2 ^ 64) * -36893488147419103234/5 के CCCCCCCCCCCCCCCDरूप में परिवर्तित होता है uint64_t
chux -

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

20
इस भयानक लेख को पढ़ें: लेबर ऑफ डिवीजन
जस्टर

9
कुछ कंपाइलर वास्तव में अक्षम कोड उत्पन्न करेंगे क्योंकि अनुकूलन अक्षम है। विशेष रूप से, वे इसे डिबगिंग को आसान बनाने के लिए करेंगे, जैसे कि कोड की अलग-अलग लाइनों पर ब्रेकप्वाइंट सेट करने की क्षमता। जीसीसी वास्तव में, बल्कि इसमें असामान्य है कि इसमें "कोई अनुकूलन नहीं" मोड है, क्योंकि इसके कई अनुकूलन संवैधानिक रूप से चालू हैं। यह एक उदाहरण है जहां आप जीसीसी के साथ देख सकते हैं। दूसरी ओर, क्लेंग, और MSVC, एक निर्देश का उत्सर्जन करेगा । (सीसी @ क्लिफर्ड)div-O0
कोड़ी ग्रे

जवाबों:


169

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

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

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

परिणामस्वरूप, आप केवल देखने के लिए divया idivआउटपुट में होते हैं यदि भाजक को संकलन-समय पर नहीं जाना जाता है।

संकलक इन अनुक्रमों को कैसे उत्पन्न करता है, इस बारे में जानकारी के लिए, साथ ही साथ आप उन्हें अपने लिए उत्पन्न करने के लिए कोड (लगभग निश्चित रूप से अनावश्यक जब तक कि आप एक साहसी संकलक के साथ काम नहीं कर रहे हैं), libdivide देखें ।


5
मुझे यकीन नहीं है कि एफपी और पूर्णांक संचालन को एक साथ तुलना, @fuz में एक साथ गांठ करना उचित है। शायद Sneftel यह कहना चाहिए कि विभाजन सबसे धीमा पूर्णांक ऑपरेशन है जिसे आप एक आधुनिक प्रोसेसर पर कर सकते हैं? साथ ही, इस "जादू" के और स्पष्टीकरण के लिए कुछ लिंक टिप्पणियों में प्रदान किए गए हैं। क्या आपको लगता है कि वे दृश्यता के लिए आपके उत्तर में एकत्र करना उचित होगा? 1 , 2 , 3
कोड़ी ग्रे

1
क्योंकि संचालन का क्रम कार्यात्मक रूप से समान है ... यह हमेशा एक आवश्यकता है, यहां तक ​​कि -O3। संकलक को कोड बनाना पड़ता है जो सभी संभावित इनपुट मानों के लिए सही परिणाम देता है। यह केवल फ़्लोटिंग पॉइंट के लिए बदलता है -ffast-math, और AFAIK में कोई "खतरनाक" पूर्णांक अनुकूलन नहीं हैं। (ऑप्टिमाइज़ सक्षम होने के साथ, कंपाइलर संभव मानों की संभावित सीमा के बारे में कुछ साबित करने में सक्षम हो सकता है जो इसे कुछ ऐसा उपयोग करने देता है जो केवल गैर-नकारात्मक हस्ताक्षरित पूर्णांक के लिए काम करता है।)
पीटर कॉर्ड्स

6
असली जवाब यह है कि gcc -O0 अभी भी कोड को मशीन कोड में बदलने के हिस्से के रूप में आंतरिक अभ्यावेदन के माध्यम से बदल देता है । यह सिर्फ ऐसा होता है कि मॉड्यूलर गुणक व्युत्क्रम डिफ़ॉल्ट रूप से -O0(लेकिन नहीं भी) द्वारा सक्षम होते हैं -Os। अन्य कंपाइलर (जैसे क्लैंग) गैर-शक्ति-के -2 स्थिरांक के लिए डीआईवी का उपयोग करेंगे -O0। संबंधित: मुझे लगता है कि मैंने अपने Collatz-conjecture हाथ से लिखे गए उत्तर
पीटर कॉर्ड

6
@PeterCordes और हाँ, मुझे लगता है कि जीसीसी (और बहुत सारे अन्य कंपाइलर) "अनुकूलन के अक्षम होने पर किस प्रकार के अनुकूलन लागू होते हैं" के लिए एक अच्छा तर्क के साथ आना भूल गए हैं। एक अस्पष्ट कोडेन बग को ट्रैक करते हुए एक दिन का बेहतर हिस्सा बिताने के बाद, मैं उस समय थोड़ा परेशान हूं।
स्नेफेल

9
@ स्नेफ़ेल: यह शायद सिर्फ इसलिए है क्योंकि आवेदनकर्ताओं की संख्या जो सक्रिय रूप से संकलक डेवलपर्स से शिकायत करते हैं कि उनके कोड के बारे में अपेक्षा से अधिक तेजी से चल रहा है अपेक्षाकृत छोटा है।
dan04

121

5 से विभाजित करना 1/5 को गुणा करने के समान है, जो फिर से 4/5 से गुणा करने और दाएं 2 बिट्स को स्थानांतरित करने के समान है। संबंधित मूल्य CCCCCCCCCCCCCCCDहेक्स में है, जो कि हेक्साडेसिमल बिंदु (यानी चार पंद्रह के लिए बाइनरी 0.110011001100आवर्ती है - क्यों के लिए नीचे देखें) के बाद 4/5 के द्विआधारी प्रतिनिधित्व है । मुझे लगता है कि आप इसे यहाँ से ले जा सकते हैं! आप निश्चित बिंदु अंकगणित की जांच करना चाह सकते हैं (हालांकि ध्यान दें कि यह अंत में पूर्णांक तक गोल है।

क्यों, विभाजन की तुलना में गुणा अधिक तेज़ है, और जब विभाजक तय हो जाता है, तो यह तेज़ मार्ग है।

फिक्स्ड-पॉइंट के संदर्भ में, यह कैसे काम करता है, इसके बारे में विस्तृत राइटअप के लिए एक ट्यूटोरियल, पारस्परिक गुणन देखें । यह दर्शाता है कि पारस्परिक कार्यों को खोजने के लिए एल्गोरिथ्म और हस्ताक्षरित विभाजन और मोडुलो को कैसे संभालना है।

आइए एक मिनट के लिए विचार करें कि क्यों 0.CCCCCCCC...(हेक्स) या 0.110011001100...बाइनरी 4/5 है। बाइनरी प्रतिनिधित्व को 4 से विभाजित करें (दाएं 2 स्थानों को शिफ्ट करें), और हम प्राप्त करेंगे 0.001100110011...जो तुच्छ निरीक्षण द्वारा प्राप्त करने के लिए मूल जोड़ा जा सकता है 0.111111111111..., जो स्पष्ट रूप से 1 के बराबर है, 0.9999999...दशमलव में उसी तरह से एक के बराबर है। इसलिए, हम जानते हैं कि x + x/4 = 1, इसलिए 5x/4 = 1,x=4/5 । इसे फिर CCCCCCCCCCCCDगोलाई के लिए हेक्स के रूप में दर्शाया जाता है (जैसा कि पिछले एक वर्तमान से परे द्विआधारी अंक होगा 1)।


2
@ user2357112 अपना उत्तर पोस्ट करने के लिए स्वतंत्र महसूस करते हैं, लेकिन मैं सहमत नहीं हूं। आप एक 64.0 बिट को 0.64 बिट से गुणा करके 128 बिट फिक्स्ड पॉइंट आंसर दे सकते हैं, जिसमें से सबसे कम 64 बिट्स को छोड़ दिया जाता है, फिर 4 से विभाजन (जैसा कि मैं पहले पैरा में इंगित करता हूं)। आप एक वैकल्पिक मॉड्यूलर अंकगणितीय उत्तर के साथ आने में सक्षम हो सकते हैं जो बिट आंदोलनों को समान रूप से अच्छी तरह से समझाता है, लेकिन मुझे पूरा यकीन है कि यह स्पष्टीकरण के रूप में काम करता है।
१igh

6
मूल्य वास्तव में "CCCCCCCCCCCCCCCD" है अंतिम D महत्वपूर्ण है, यह सुनिश्चित करता है कि जब परिणाम को काट दिया जाता है तो सटीक विभाजन सही उत्तर के साथ सामने आते हैं।
प्लगव्यू

4
कोई बात नहीं। मैंने नहीं देखा कि वे 128-बिट गुणन परिणाम के ऊपरी 64 बिट्स ले रहे हैं; यह ऐसा कुछ नहीं है जो आप ज्यादातर भाषाओं में कर सकते हैं, इसलिए मुझे शुरू में यह महसूस नहीं हुआ कि यह हो रहा है। यह उत्तर एक स्पष्ट उल्लेख से बहुत बेहतर होगा कि कैसे 128-बिट परिणाम के ऊपरी 64 बिट्स को एक निश्चित-बिंदु संख्या से गुणा करने और नीचे गोल करने के बराबर है। (इसके अलावा, यह स्पष्ट करना अच्छा होगा कि 1/5 के बजाय 4/5 क्यों होना चाहिए, और हमें नीचे के बजाय 4/5 को गोल क्यों करना है।)
user2357112

2
Afaict आपको यह काम करना होगा कि एक राउंडिंग बाउंड्री के ऊपर एक डिवीजन को 5 से ऊपर फेंकने के लिए कितनी बड़ी त्रुटि की आवश्यकता होती है, फिर तुलना करें कि आपके कैक्ल्यूलेशन में सबसे खराब स्थिति है। वास्तव में gcc डेवलपर्स ने ऐसा किया है और निष्कर्ष निकाला है कि यह हमेशा सही परिणाम देगा।
प्लगव्यू

3
वास्तव में आपको संभावित रूप से केवल 5 उच्चतम संभव इनपुट मानों की जांच करने की आवश्यकता है, अगर वे गोल सही ढंग से बाकी सब कुछ भी होना चाहिए।
प्लगवॉश

60

सामान्य गुणा में विभाजन की तुलना में बहुत तेज है। इसलिए अगर हम पारस्परिक रूप से गुणा करके दूर जा सकते हैं तो इसके बजाय हम एक स्थिर से विभाजन को काफी तेज कर सकते हैं

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

-3689348814741910323 0xCCCCCCCCCCCCCD है, जो 0.64 निश्चित बिंदु में व्यक्त 4/5 से अधिक का मान है।

जब हम एक 64 बिट पूर्णांक को 0.64 निश्चित बिंदु संख्या से गुणा करते हैं तो हमें 64.64 परिणाम मिलता है। हम एक 64-बिट पूर्णांक (प्रभावी रूप से इसे शून्य की ओर गोल करते हुए) के मान को छोटा करते हैं और फिर एक और बदलाव करते हैं जो फिर से चार से विभाजित हो जाता है और फिर से बिट स्तर को देखकर यह स्पष्ट होता है कि हम दोनों ट्रंक को एक ट्रंक के रूप में मान सकते हैं।

यह स्पष्ट रूप से हमें कम से कम 5 तक विभाजन का अनुमान देता है, लेकिन क्या यह हमें एक सटीक उत्तर सही ढंग से शून्य की ओर देता है?

सटीक उत्तर प्राप्त करने के लिए त्रुटि को छोटा करने के लिए पर्याप्त होना चाहिए ताकि एक गोल सीमा पर उत्तर को धक्का न दिया जा सके।

5 द्वारा विभाजन का सटीक उत्तर हमेशा 0, 1/5, 2/5, 3/5 या 4/5 का एक आंशिक भाग होगा। इसलिए गुणा और स्थानांतरित परिणाम में 1/5 से कम की सकारात्मक त्रुटि कभी भी परिणाम को गोल सीमा से अधिक नहीं धकेलती है।

हमारे निरंतर में त्रुटि (1/5) * 2 -64 हैI का मान 2 64 से कम है इसलिए गुणा करने के बाद त्रुटि 1/5 से कम है। 4 से विभाजन के बाद त्रुटि से (1/5) * 2 कम है -2

(1/5) * 2 −2 <1/5 इसलिए उत्तर हमेशा एक सटीक विभाजन करने और शून्य की ओर गोल करने के बराबर होगा।


दुर्भाग्य से यह सभी विभाजकों के लिए काम नहीं करता है।

अगर हम शून्य से दूर गोलाई के साथ 0.64 निश्चित बिंदु संख्या के रूप में 4/7 का प्रतिनिधित्व करने की कोशिश करते हैं, तो हम (6%) * 2 की त्रुटि के साथ समाप्त होते हैं -64 की । सिर्फ 2 64 के एक i मान से गुणा करने के बाद, हम 6/7 के तहत एक त्रुटि के साथ समाप्त होते हैं और चार से विभाजित करने के बाद हम केवल 1.5 / 7 के नीचे एक त्रुटि के साथ समाप्त होते हैं जो 1/7 से अधिक है।

तो 7 को सही ढंग से विभाजन को लागू करने के लिए हमें 0.65 निश्चित बिंदु संख्या से गुणा करना होगा। हम अपने निर्धारित बिंदु संख्या के निचले 64 बिट्स से गुणा करके, फिर मूल संख्या को जोड़ सकते हैं (यह कैरी बिट में ओवरफ्लो हो सकता है) फिर कैरी के माध्यम से घुमाता है।


8
यह उत्तर "गणित से मॉड्यूलर गुणात्मक व्युत्क्रम को बदल देता है जो मुझे लगता है कि" कुछ करने के लिए समय लेना चाहता है की तुलना में अधिक जटिल लग रहा है कि समझ में आता है। समझने में आसान संस्करण के लिए +1। मुझे कंपाइलर-जनरेटेड कॉन्स्टेंट के उपयोग के अलावा कुछ भी करने की आवश्यकता नहीं है, इसलिए मैंने केवल गणित को समझाने वाले अन्य लेखों को स्किम्ड किया है।
पीटर कॉर्ड्स

2
मुझे कोड में मॉड्यूलर अंकगणित के साथ कुछ भी करने के लिए कुछ भी नहीं दिखता है। दुन्नो जहां से कुछ अन्य टिप्पणीकार मिल रहे हैं।
प्लगव्यू

3
यह एक रजिस्टर में सभी पूर्णांक गणित की तरह modulo 2 ^ n है। en.wikipedia.org/wiki/…
पीटर कॉर्ड्स

4
@PeterCordes मॉड्यूलर गुणक व्युत्क्रम का उपयोग सटीक विभाजन के लिए किया जाता है, afaik वे सामान्य विभाजन के लिए उपयोगी नहीं हैं
हेरोल्ड

4
@PeterCord फिक्स्ड-पॉइंट पारस्परिक द्वारा गुणा? मुझे नहीं पता कि हर कोई इसे कॉल करता है, लेकिन मैं शायद इसे कॉल करूंगा, यह काफी वर्णनात्मक है
हेरोल्ड

12

यहां एक एल्गोरिथ्म के दस्तावेज़ से लिंक किया गया है जो विज़ुअल स्टूडियो (ज्यादातर मामलों में) के साथ देखे जाने वाले मूल्यों और कोड का उत्पादन करता है और मुझे लगता है कि अभी भी जीसीसी में एक स्थिर पूर्णांक द्वारा एक चर पूर्णांक के विभाजन के लिए उपयोग किया जाता है।

http://gmplib.org/~tege/divcnst-pldi94.pdf

लेख में, एक तलवार के पास N बिट्स होते हैं, एक udword में 2N बिट्स, n = अंश = लाभांश, d = भाजक = भाजक होता है, initially शुरू में छत (log2 (d)) पर सेट किया जाता है, shpre प्री-शिफ्ट (गुणा करने से पहले उपयोग किया जाता है) ) = e = शून्य बिट्स को अनुगामी बनाने की संख्या, shpost पोस्ट-शिफ्ट (गुणा के बाद उपयोग किया जाता है), सटीक परिशुद्धता है = N - e = N - shpre। लक्ष्य पूर्व-पाली, गुणा और बाद की पाली का उपयोग करके n / d की गणना का अनुकूलन करना है।

6.2 नीचे स्क्रॉल करें, जो यह बताता है कि एक udword गुणक (अधिकतम आकार N + 1 बिट्स है), कैसे उत्पन्न होता है, लेकिन इस प्रक्रिया को स्पष्ट रूप से नहीं बताता है। मैं इसे नीचे समझाता हूँ।

चित्र ४.२ और आंकड़ा ६.२ यह दर्शाता है कि अधिकांश भाजक के लिए गुणक को N बिट या कम गुणक में कैसे घटाया जा सकता है। समीकरण 4.5 बताता है कि कैसे फॉर्मूला 4.1 और 4.2 में एन + 1 बिट मल्टीप्लायरों से निपटने के लिए इस्तेमाल किया गया था।

आधुनिक X86 और अन्य प्रोसेसर के मामले में, गुणा समय निश्चित है, इसलिए प्री-शिफ्ट इन प्रोसेसर पर मदद नहीं करता है, लेकिन यह अभी भी गुणक को N + 1 बिट से N बिट तक कम करने में मदद करता है। मुझे नहीं पता कि क्या GCC या Visual Studio ने X86 लक्ष्य के लिए पूर्व-शिफ्ट को समाप्त कर दिया है।

चित्रा 6.2 पर वापस जाना। Mlow और mhigh के लिए अंश (डिविडेंड) एक udword से बड़ा तभी हो सकता है जब भाजक (भाजक)> 2 ^ (N-1) (जब ℓ == N => mlow = 2 ^ (2N)), इस मामले में n / d के लिए अनुकूलित प्रतिस्थापन एक तुलना है (यदि n> = d, q = 1, अन्यथा q = 0), तो कोई गुणक उत्पन्न नहीं होता है। Mlow और mhigh के प्रारंभिक मान N + 1 बिट्स होंगे, और दो udword / uword डिवाइसेस का उपयोग प्रत्येक N + 1 बिट वैल्यू (mlow या mhigh) के उत्पादन के लिए किया जा सकता है। उदाहरण के रूप में 64 बिट मोड में X86 का उपयोग करना:

; upper 8 bytes of dividend = 2^(ℓ) = (upper part of 2^(N+ℓ))
; lower 8 bytes of dividend for mlow  = 0
; lower 8 bytes of dividend for mhigh = 2^(N+ℓ-prec) = 2^(ℓ+shpre) = 2^(ℓ+e)
dividend  dq    2 dup(?)        ;16 byte dividend
divisor   dq    1 dup(?)        ; 8 byte divisor

; ...
        mov     rcx,divisor
        mov     rdx,0
        mov     rax,dividend+8     ;upper 8 bytes of dividend
        div     rcx                ;after div, rax == 1
        mov     rax,dividend       ;lower 8 bytes of dividend
        div     rcx
        mov     rdx,1              ;rdx:rax = N+1 bit value = 65 bit value

आप जीसीसी के साथ इसका परीक्षण कर सकते हैं। आप पहले से ही देख रहे हैं कि j = i / 5 कैसे संभाला जाता है। देखिए कैसे j = i / 7 को संभाला जाता है (जो कि N + 1 बिट गुणक केस होना चाहिए)।

अधिकांश वर्तमान प्रोसेसर पर, एक निश्चित समय में गुणा करना होता है, इसलिए प्री-शिफ्ट की आवश्यकता नहीं होती है। X86 के लिए, अंतिम परिणाम अधिकांश भाजक के लिए एक दो अनुदेश अनुक्रम है, और 7 (जैसे एक एन + 1 बिट गुणक का अनुकरण करने के लिए 5 के लिए पांच अनुदेश अनुक्रम अनुक्रम समीकरण 4.5 और पीडीएफ फाइल के आंकड़े 4.2 में दिखाया गया है)। उदाहरण X86-64 कोड:

;       rax = dividend, rbx = 64 bit (or less) multiplier, rcx = post shift count
;       two instruction sequence for most divisors:

        mul     rbx                     ;rdx = upper 64 bits of product
        shr     rdx,cl                  ;rdx = quotient
;
;       five instruction sequence for divisors like 7
;       to emulate 65 bit multiplier (rbx = lower 64 bits of multiplier)

        mul     rbx                     ;rdx = upper 64 bits of product
        sub     rbx,rdx                 ;rbx -= rdx
        shr     rbx,1                   ;rbx >>= 1
        add     rdx,rbx                 ;rdx = upper 64 bits of corrected product
        shr     rdx,cl                  ;rdx = quotient
;       ...

वह कागज इसे gcc में लागू करने का वर्णन करता है, इसलिए मुझे लगता है कि यह एक सुरक्षित धारणा है कि एक ही अहंकार अभी भी उपयोग किया जाता है।
पीटर कॉर्ड्स

उस पेपर दिनांक १ ९९ ४ में इसे gcc में लागू करने का वर्णन किया गया है, इसलिए gcc के अपने एल्गोरिदम को अपडेट करने का समय आ गया है। ठीक उसी स्थिति में जब दूसरों के पास यह देखने के लिए जाँच करने का समय नहीं है कि उस URL में 94 का क्या अर्थ है।
एड ग्रिम

0

मैं थोड़ा अलग कोण से उत्तर दूंगा: क्योंकि इसे करने की अनुमति है।

C और C ++ को एक अमूर्त मशीन के खिलाफ परिभाषित किया गया है। कंपाइलर इस प्रोग्राम को एब्सट्रैक्ट मशीन के संदर्भ में कंक्रीट मशीन के रूप में बदल देता है , जैसा कि अगर नियम है।

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