2013-07-22 5 views
7

जैसा कि "Integer division rounding with negatives in C++" में उद्धृत किया गया है, सी 99 से पहले सी (यानी सी 8 9 में) और सी ++ में सी ++ में (यानी सी ++ 98 और सी ++ 03) से पहले, एक पूर्णांक विभाजन गणना के लिए जहां या तो ऑपरेंड नकारात्मक है शेष का चिह्न (या समकक्ष, मात्रात्मक की गोल दिशा) कार्यान्वयन-परिभाषित है।glibc `div()` कोड में बग?

तो मानक समारोह std::div जो को निर्दिष्ट किया जाता है शून्य की ओर भागफल काटना (अर्थात शेष लाभांश के रूप में ही चिह्न (अंश) है) (उदाहरण के this answer to "what is purpose of div() library function?" के लिए देखें) आता है।

-

(। अंत टिप्पणी

typedef struct 
    { 
    int quot; 
    int rem; 
    } div_t; 

नोट:: div_t के रूप में परिभाषित किया गया है)

यहाँ div() (source) के लिए glibc का कोड (भी "Is div function useful (stdlib.h)?" में उद्धृत) है

/* Return the `div_t' representation of NUMER over DENOM. */ 
div_t 
div (numer, denom) 
    int numer, denom; 
{ 
    div_t result; 

    result.quot = numer/denom; 
    result.rem = numer % denom; 

    /* The ANSI standard says that |QUOT| <= |NUMER/DENOM|, where 
    NUMER/DENOM is to be computed in infinite precision. In 
    other words, we should always truncate the quotient towards 
    zero, never -infinity. Machine division and remainer may 
    work either way when one or both of NUMER or DENOM is 
    negative. If only one is negative and QUOT has been 
    truncated towards -infinity, REM will have the same sign as 
    DENOM and the opposite sign of NUMER; if both are negative 
    and QUOT has been truncated towards -infinity, REM will be 
    positive (will have the opposite sign of NUMER). These are 
    considered `wrong'. If both are NUM and DENOM are positive, 
    RESULT will always be positive. This all boils down to: if 
    NUMER >= 0, but REM < 0, we got the wrong answer. In that 
    case, to get the right answer, add 1 to QUOT and subtract 
    DENOM from REM. */ 

    if (numer >= 0 && result.rem < 0) 
    { 
     ++result.quot; 
     result.rem -= denom; 
    } 

    return result; 
} 

जैसा कि आप देख सकते हैं कि बड़ी टिप्पणी ब्लॉक के बाद एक परीक्षण है, जिसका उद्देश्य परिणाम को "सही" करना है यदि अंतर्निहित डिवीसी शून्य की बजाय अनंत की तरफ झुकाव पर।

अब सवाल:

वहाँ कि कोड में एक बग नहीं है?

आइए पहले उदाहरण कॉल div(42, -5) पर विचार करें। "गणित में" 42/-5 बिल्कुल -8.4, तो सैद्धांतिक रूप से C89 में है और सी ++ 03 42/-5 उत्पन्न हो सकते हैं या तो -8 (छोटा) या (फिदा) क्रियान्वयन के आधार पर। कोड पढ़ना:

  • तो 42/-5 पैदावार -8 तो 42 % -5 पैदावार 2 (42 == -8 * -5 + 2 के रूप में), इसलिए परीक्षण (42 >= 0 && 2 < 0) है जो सच नहीं है और इसके बाद के संस्करण समारोह -8 और 2 देता है, के रूप में चाहते थे;
  • 42/-5 तो पैदावार तो 42 % -5 पैदावार -3 (42 == -9 * -5 + -3 के रूप में), इसलिए परीक्षण (42 >= 0 && -3 < 0) जो सच है है, इसलिए ऊपर फ़ंक्शन -9 + 1 और -3 - -5, अर्थात -8 और 2 "सुधार", के रूप में करना चाहता था।

लेकिन अब के कॉल div(-42, 5) (उल्टे संकेत) पर विचार करते हैं:

  • तो -42/5 पैदावार -8 तो -42 % 5 पैदावार -2 (-42 == -8 * 5 + -2 के रूप में), इसलिए परीक्षण (-42 >= 0 && -2 < 0) जो सच नहीं है और इसके बाद के संस्करण समारोह है -8 और -2 लौटाता है;
  • -42/5 तो पैदावार तो -42 % 5 पैदावार 3 (-42 == -9 * 5 + 3 के रूप में), इसलिए परीक्षण (-42 >= 0 && 3 < 0) जो ... नहीं सच है! और उपरोक्त फ़ंक्शन और 3-8 और -2 के बदले लौटाता है!

पहले उपरोक्त कोड में टिप्पणी सही लगता है जब यह कहते हैं स्थिति यह है कि एक सुधार की जरूरत है कि जब "रेम numer के विपरीत संकेत है", लेकिन तब यह विशाल सरलीकरण "यह सब फोड़े बनाता है नीचे: यदि NUMER> = 0, लेकिन आरईएम < 0, हमें गलत जवाब मिला ", जो मुझे गलत (अधूरा) लगता है क्योंकि यह मामले को छोड़ देता है" अगर NUMER < 0, लेकिन आरईएम> 0 "(-42 और पिछले उदाहरण में)।

मैं शायद ही विश्वास है कि इस तरह के एक बग 1992 या 1990 के बाद से किसी का ध्यान नहीं बना रहा है | (जाहिरा तौर पर someone tried to "fix" it लेकिन यह अभी भी गलत लगता है क्योंकि div(-42, 5)-10 और 8 लौट सकता है) ... बेशक, अधिकांश प्रयोगों शून्य की ओर डिफ़ॉल्ट रूप से छोटा कर दिया गया है सकते हैं (और सभी को C99 और C++ 11 से ऐसा करने की आवश्यकता है, इसलिए समस्या नवीनतम मानकों में "moot" है) इसलिए बग उन पर प्रकट नहीं होगा, लेकिन फिर भी ... शायद मैं ' मैं यहाँ कुछ याद कर रहा हूँ?

किसी भी अंतर्दृष्टि के लिए धन्यवाद।


(संपादित) के लिए के रूप में "मुद्दा सी ++ 11 और C99 (और नए) में विवादास्पद है": तदनुसार, इन मानकों में निर्मित विभाजन की ओर काट-छांट करने की आवश्यकता है शून्य, इसलिए हमें परिणाम को समायोजित करने की आवश्यकता नहीं है, लेकिन इसका मतलब यह नहीं है कि वर्तमान कार्यान्वयन आवश्यक और अनावश्यक रूप से अक्षम से अधिक जटिल है? "बड़ी टिप्पणी" अप्रचलित है और if बेकार परीक्षण है, तो क्या उस हिस्से को पूरी तरह से हटाया जाना चाहिए?

+0

सापेक्ष ऑपरेटर कार्यान्वयन निर्भर है। मुझे लगता है कि इस मुद्दे का मुख्य क्रूक्स है। – Jiminion

+0

@ जिम 'div' का उद्देश्य कार्यान्वयन-निर्भर संचालन को कार्यान्वित करने के लिए कार्यान्वयन-निर्भर संचालन को ठीक करना है- _independent_ परिणाम। @ouah आपका लिंक मेरे प्रश्न (http://minilib-c.googlecode.com/svn-history/r2/trunk/stdlib/div.c) के अंतिम लिंक के समान कोड है और जैसा कि मैंने कहा, 'num के लिए == -42' और 'denom == 5' अगर आपको' r.quot = -9' और 'r.rem = 3' मिलता है, तो नया" सुधार "' --r.quot' और 'r.rem + = 5' 'r.quot == -10' और' r.rem == 8' ('r.quot == -8' और' r.rem == -2') के बजाय ... –

+0

देगा मुझे लगता है कि उन्होंने इसे एक और तरीका देखा। उन्होंने div ठीक w.r.t. परिभाषित किया %, जिसे उन्होंने स्वीकार किया था कार्यान्वयन निर्भर था। (मिश्रित साइन ऑपरेटर के साथ मॉड्यूलो ऑपरेशन क्यों करते हैं? मुझे नहीं लगता कि यह एक भारी ट्रोड क्षेत्र था, इसलिए स्थिरता के बारे में थोड़ी चिंता नहीं थी। – Jiminion

उत्तर

5

कोड के मूल लेखक के रूप में, मुझे कहना है: आप सही हैं। यह टूटा हुआ है। हमारे पास परीक्षण के लिए "गलत तरीका" नहीं था, और मैंने शायद दिन या कुछ में उपरोक्त बहुत देर से (या शुरुआती ...) लिखा था।

हम नए मानकों से बचाए जाते हैं, और यदि आवश्यक हो तो प्री-सी 99 के लिए शायद #ifdef (और एडजस्टेड कोड को सही किया गया) के साथ पूरी चीज साफ होनी चाहिए।

(इसके अलावा, मैं ध्यान दें जाएगा कि मूल जीएनयू शैली खरोज :-) साथ इंडेंट नहीं था) पर हस्ताक्षर किए ऑपरेंड साथ

+0

क्रिस टोरेक! यह आप हैं_! अपने जवाब के लिए आपको बहुत बहुत धन्यवाद! वास्तव में मैं एक बेहतर स्रोत की उम्मीद नहीं कर सका :) (इंडेंटेशन के लिए, शायद मूल इसके करीब था [div.c] (http://svnweb.freebsd.org/base/head/lib/libc/stdlib/ div.c? view = सह)?) (इसके अलावा, इस बीच में मैं और अधिक वेब खोज कर रहा हूं, और पाया [एक और "फिक्स"] (http://www.beedub.com/Sprite093/src/lib/ सी/stdlib/div.c) (एक्सओआर कानूनी लगता है लेकिन समायोजन गलत है), और विशेष रूप से ** [1 9 87 से एक कोड] (https://raw.github.com/7shi/minix-tools/master/lib/ सी/ansi/div.c) ** (!) जो 42/-5 _and_ -42/5 दोनों को सही ढंग से संभालने लगता है!) –

+1

हां, यह मूल रूप से मूल है (के एंड आर-आश, "उच्च Bosticity" इंडेंटेशन :-) ... अंततः फ्रीबीएसडी में शैली (9) बन गई)। मैंने कभी स्प्राइट संस्करण नहीं देखा, न ही पहले Vrije Universiteit एक। एक्सओआर सादे (हस्ताक्षरित) 'int' के साथ जोखिम भरा लगता है। 'स्थैतिक' विधि मान्य है, लेकिन सभी मामलों में रन-टाइम परीक्षण की आवश्यकता बस किसी भी तरह गन्दा लगती है। यहां तक ​​कि अगर हम सी 99 पर भरोसा नहीं कर सकते हैं तो यह भी अच्छा होगा कि एक कंपाइलर-विशिष्ट # परिभाषित पूर्णांक विभाजन व्यवहार का वर्णन करें, क्योंकि बहुत सी मशीनें "सी 99 चाहता है जिस तरह से व्यवहार करती है"। वास्तव में – torek

+0

। ** मैंने एक [glibc बग रिपोर्ट] दायर की है (http://sourceware.org/bugzilla/show_bug.cgi?id=15799)। ** (वैसे, Vrije Universiteit एक करीबी था, लेकिन उदाहरण के लिए मशीन पर जो कि हमेशा '{-2, 0}' उत्पन्न करता है, जो '{-2, 0}' उत्पन्न करता है, '{-1, -5} '... को गलत तरीके से" सही "किया जाएगा, –

-1

लेकिन इस मामले में अंक -42 के बराबर है, (इसलिए यह नकारात्मक है) इस स्थिति को स्वचालित रूप से झूठी बना दिया गया। वास्तव में आप एक सरल कोड है जो समारोह आप देख हैं पर अमल करने की कोशिश करता है, तो यह है कि (यह डिबगिंग) अगर बयान: enter link description here

मुझे आशा है कि:

// false in this case, numer = -42 and result.rem = -2 
if(numer >= 0 && result.rem < 0) 

यहाँ आप संकलित परिणाम देख सकते हैं उत्तर सही है और आपको त्रुटि को समझने में मदद करेगा।

+1

समस्या यह है कि जाहिर है कि स्थिति इस मामले में झूठी नहीं होनी चाहिए। –

+0

क्षमा करें, लेकिन ऐसा लगता है कि आपने इस मुद्दे को गलत समझा। @ मार्कब हां। –

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