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