अगर (a - b <0) और if के बीच अंतर (a <b)


252

मैं जावा के ArrayListसोर्स कोड को पढ़ रहा था और इफ-स्टेटमेंट्स में कुछ तुलनाओं पर ध्यान दिया।

जावा 7 में, विधि grow(int)का उपयोग करता है

if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;

जावा 6 में, growमौजूद नहीं था। ensureCapacity(int)हालांकि विधि का उपयोग करता है

if (newCapacity < minCapacity)
    newCapacity = minCapacity;

बदलाव के पीछे क्या कारण था? क्या यह प्रदर्शन का मुद्दा था या सिर्फ एक शैली?

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


35
@Tunaki ओवरफ्लो को रोकने के मामले में कैसे if (newCapacity - minCapacity < 0)बेहतर है if (newCapacity < minCapacity)?
एरन

3
मुझे आश्चर्य है कि क्या उल्लेखित संकेत अतिप्रवाह वास्तव में कारण है। अतिप्रवाह अधिक बहाव के लिए एक उम्मीदवार लगता है। घटक शायद "यह फिर भी अतिप्रवाह नहीं होगा" कह रहा है, शायद दोनों चर गैर-नकारात्मक हैं।
जोप एगेनजेन

12
FYI करें, आप मानते हैं कि तुलना करना "पूर्ण घटाव" करने की तुलना में तेज़ है। मेरे अनुभव में, मशीन कोड स्तर पर, आमतौर पर तुलना एक घटाव का प्रदर्शन करके की जाती है, परिणाम को फेंकने और परिणामी झंडों की जांच करने के लिए।
डेविड डुबोइस

6
@ डेविड डुबोइस: ओपी ने यह नहीं माना कि तुलना घटाव की तुलना में तेज है, लेकिन शून्य के साथ तुलना दो मनमाने मूल्यों की तुलना में तेज हो सकती है और यह भी सही ढंग से मानती है कि जब आप वास्तविक घटाव का प्रदर्शन कर रहे हैं तो यह पकड़ में नहीं आता है। शून्य के साथ तुलना करने के लिए एक मूल्य प्राप्त करने के लिए। यह सब काफी उचित है।
होल्गर

जवाबों:


285

a < bऔर a - b < 0दो अलग-अलग चीजों का मतलब हो सकता है। निम्नलिखित कोड पर विचार करें:

int a = Integer.MAX_VALUE;
int b = Integer.MIN_VALUE;
if (a < b) {
    System.out.println("a < b");
}
if (a - b < 0) {
    System.out.println("a - b < 0");
}

जब चलाया जाएगा, तो यह केवल प्रिंट करेगा a - b < 0। जो होता है वह a < bस्पष्ट रूप से गलत होता है , लेकिन अति a - bहो जाता है और -1नकारात्मक हो जाता है।

अब, यह कहते हुए कि, विचार करें कि सरणी की लंबाई है जो वास्तव में करीब है Integer.MAX_VALUE। कोड ArrayListइस प्रकार है:

int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity);

oldCapacityकरने के लिए वास्तव में करीब है Integer.MAX_VALUEतो newCapacity(जो oldCapacity + 0.5 * oldCapacity) अतिप्रवाह और हो सकता है Integer.MIN_VALUE(यानी नकारात्मक)। फिर, घटाकर minCapacity underflows एक सकारात्मक संख्या में वापस।

यह जाँच सुनिश्चित करती है कि ifनिष्पादित नहीं किया गया है। यदि कोड के रूप में लिखा गया था if (newCapacity < minCapacity), तो यह trueइस मामले में होगा (चूंकि newCapacityनकारात्मक है) इसलिए की परवाह किए बिना newCapacityमजबूर किया जाएगा ।minCapacityoldCapacity

इस अतिप्रवाह मामले को अगले द्वारा नियंत्रित किया जाता है यदि। जब newCapacityअतिप्रवाह हुआ है, तो यह होगा true: MAX_ARRAY_SIZEके रूप में परिभाषित किया गया है Integer.MAX_VALUE - 8और Integer.MIN_VALUE - (Integer.MAX_VALUE - 8) > 0है truenewCapacityइसलिए ठीक ही नियंत्रित किया जाता है: hugeCapacityविधि रिटर्न MAX_ARRAY_SIZEया Integer.MAX_VALUE

NB: यह // overflow-conscious codeइस पद्धति में टिप्पणी कह रहा है।


8
गणित और सीएस के बीच के अंतर पर अच्छा डेमो
पिग्गीबॉक्स

36
@piggybox मैं ऐसा नहीं कहूंगा। यह गणित है। यह सिर्फ Z में गणित नहीं है, लेकिन पूर्णांक के एक संस्करण में modulo 2 ^ 32 (विहित निरूपण के साथ सामान्य से अलग चुना गया है)। यह एक उचित गणितीय प्रणाली है, न कि केवल "योग्य कंप्यूटर और उनके quirks"।
हेरोल्ड

2
मैंने कोड लिखा होगा जो ओवरफ्लो नहीं हुआ।
अलेक्जेंडर डबलिनस्की

IIRC प्रोसेसर साइन किए गए पूर्णांकों पर कम-से-कम निर्देश लागू करते हैं a - bऔर यह जाँच कर करते हैं कि क्या शीर्ष बिट एक है 1। वे अतिप्रवाह कैसे संभालते हैं?
बेन लेगियरियो

2
@ BenC.R.Leggiero x86, दूसरों के बीच, सशर्त निर्देशों के साथ उपयोग के लिए एक अलग रजिस्टर में स्थिति झंडे के माध्यम से विभिन्न स्थितियों को ट्रैक करता है। इस रजिस्टर में परिणाम के संकेत के लिए अलग-अलग बिट्स हैं, परिणाम का शून्यकाल, और अंतिम अंकगणितीय ऑपरेशन में ओवर-/ अंडरफ़्लो हुआ या नहीं।

105

मुझे यह स्पष्टीकरण मिला :

टीयू पर, 9 मार्च, 2010 को 03:02 पर, केविन एल स्टर्न ने लिखा:

मैंने एक त्वरित खोज की और यह प्रतीत होता है कि जावा वास्तव में दो का पूरक है। फिर भी, कृपया मुझे यह इंगित करने की अनुमति दें कि, सामान्य रूप से, इस प्रकार का कोड मुझे चिंतित करता है क्योंकि मैं पूरी तरह से उम्मीद करता हूं कि किसी बिंदु पर कोई व्यक्ति साथ आएगा और वही करेगा जो डीमित्रो ने सुझाया था; वह है, कोई बदल जाएगा:

if (a - b > 0)

सेवा

if (a > b)

और पूरा जहाज डूब जाएगा। मैं, व्यक्तिगत रूप से, अस्पष्टताओं से बचना पसंद करता हूं जैसे कि पूर्णांक मेरे एल्गोरिथ्म के लिए एक अनिवार्य आधार को ओवरफ्लो करता है जब तक कि ऐसा करने का कोई अच्छा कारण न हो। मैं, सामान्य तौर पर, अतिप्रवाह से बचने के लिए और अतिप्रवाह परिदृश्य को अधिक स्पष्ट बनाने के लिए पसंद करूंगा:

if (oldCapacity > RESIZE_OVERFLOW_THRESHOLD) {
   // Do something
} else {
  // Do something else
}

अच्छी बात है।

में ArrayListहम ऐसा नहीं कर सकते (या कम से कम अनुरूपता से नहीं), क्योंकि ensureCapacityएक सार्वजनिक एपीआई है और प्रभावी रूप से पहले से ही एक सकारात्मक क्षमता है कि संतुष्ट नहीं किया जा सकता है के लिए अनुरोध के रूप में ऋणात्मक संख्याओं स्वीकार करता है।

वर्तमान एपीआई इस तरह प्रयोग किया जाता है:

int newcount = count + len;
ensureCapacity(newcount);

यदि आप अतिप्रवाह से बचना चाहते हैं, तो आपको कुछ कम प्राकृतिक परिवर्तन करने की आवश्यकता होगी

ensureCapacity(count, len);
int newcount = count + len;

वैसे भी, मैं अतिप्रवाह-सचेत कोड रख रहा हूं, लेकिन अधिक चेतावनी टिप्पणियां जोड़ रहा हूं, और "आउट-लाइनिंग" विशाल सरणी निर्माण कर रहा हूं ताकि ArrayListअब ऐसा कोड दिखता है:

/**
 * Increases the capacity of this <tt>ArrayList</tt> instance, if
 * necessary, to ensure that it can hold at least the number of elements
 * specified by the minimum capacity argument.
 *
 * @param minCapacity the desired minimum capacity
 */
public void ensureCapacity(int minCapacity) {
    modCount++;

    // Overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

/**
 * The maximum size of array to allocate.
 * Some VMs reserve some header words in an array.
 * Attempts to allocate larger arrays may result in
 * OutOfMemoryError: Requested array size exceeds VM limit
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
 * Increases the capacity to ensure that it can hold at least the
 * number of elements specified by the minimum capacity argument.
 *
 * @param minCapacity the desired minimum capacity
 */
private void grow(int minCapacity) {
    // Overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);

    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

private int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

वेबरेव ने पुनर्जीवित किया।

मार्टिन

जावा 6 में, यदि आप API का उपयोग करते हैं:

int newcount = count + len;
ensureCapacity(newcount);

और newCountओवरफ्लो (यह नकारात्मक हो जाता है), if (minCapacity > oldCapacity)झूठे वापस आ जाएगा और आप गलती से मान सकते हैं कि इसके ArrayListद्वारा वृद्धि हुई थी len


2
अच्छा विचार है, लेकिन इसके कार्यान्वयन सेensureCapacity विरोधाभास है ; यदि minCapacityनकारात्मक है, तो आप उस बिंदु पर कभी नहीं आते हैं - यह केवल चुपचाप अनदेखा है क्योंकि जटिल कार्यान्वयन को रोकने का दिखावा करता है। सार्वजनिक एपीआई अनुकूलता के लिए इसलिए "हम ऐसा नहीं कर सकते" एक अजीब तर्क है जैसा कि उन्होंने पहले ही किया था। इस व्यवहार पर भरोसा करने वाले एकमात्र कॉल आंतरिक हैं।
होल्गर

1
@Holger यदि minCapacityबहुत नकारात्मक है (यानी intArrayList के वर्तमान आकार को जोड़ने वाले तत्वों की संख्या को जोड़ने पर अतिप्रवाह के परिणामस्वरूप ) minCapacity - elementData.lengthफिर से अतिप्रवाह और सकारात्मक बनने के लिए। मैं इसे समझता हूं।
एरन

1
@ होलगर हालांकि, उन्होंने इसे जावा 8 में फिर से बदल दिया if (minCapacity > minExpand), जिसे मैं नहीं समझता।
एरन

हां, दो addAllविधियां एकमात्र मामला है जहां यह वर्तमान आकार के योग के रूप में प्रासंगिक है और नए तत्वों की संख्या अतिप्रवाह कर सकती है। फिर भी, ये आंतरिक कॉल हैं और तर्क "हम इसे बदल नहीं सकते क्योंकि ensureCapacityएक सार्वजनिक एपीआई है" एक अजीब तर्क है जब वास्तव में, ensureCapacityनकारात्मक मूल्यों को अनदेखा करता है। जावा 8 एपीआई ने उस व्यवहार को नहीं बदला, यह सब कुछ डिफ़ॉल्ट क्षमता के नीचे की क्षमताओं को अनदेखा कर रहा है जब ArrayListयह प्रारंभिक अवस्था में है (यानी डिफ़ॉल्ट क्षमता के साथ आरंभीकृत और अभी भी खाली है)।
होल्गर

दूसरे शब्दों में, इसके बारे में तर्क newcount = count + lenसही है जब यह आंतरिक उपयोग की बात आती है, हालांकि, यह publicविधि पर लागू नहीं होता है ensureCapacity()...
होल्गर

19

कोड को देखते हुए:

int newCapacity = oldCapacity + (oldCapacity >> 1);

यदि oldCapacityयह काफी बड़ा है, तो यह अतिप्रवाह होगा, और newCapacityएक नकारात्मक संख्या होगी। जैसी तुलना newCapacity < oldCapacityगलत तरीके से मूल्यांकन करेगी trueऔर ArrayListबढ़ने में विफल रहेगी।

इसके बजाय, लिखित रूप में कोड ( newCapacity - minCapacity < 0गलत रिटर्न) newCapacity, अगली पंक्ति में नकारात्मक मूल्य को और अधिक मूल्यांकन करने की अनुमति देगा , जिसके परिणामस्वरूप ( ) बढ़ने के लिए अनुमति देने के लिए ( ) को पुन: newCapacityप्राप्त करने से पुनर्गणना होगी ।hugeCapacitynewCapacity = hugeCapacity(minCapacity);ArrayListMAX_ARRAY_SIZE

यह वही है जो // overflow-conscious codeटिप्पणी करने की कोशिश कर रहा है, हालांकि विशिष्ट रूप से।

इसलिए, निचली रेखा, नई तुलना ArrayListपूर्वनिर्धारित की तुलना में बड़े को आवंटित करने से बचाती है MAX_ARRAY_SIZEजबकि जरूरत पड़ने पर उस सीमा तक सही बढ़ने की अनुमति देती है।


1

जब तक अभिव्यक्ति a - bविपरीत नहीं होती है, तब तक दोनों रूप बिल्कुल एक समान व्यवहार करते हैं । यदि aएक बड़ा नकारात्मक है, और bएक बड़ा सकारात्मक है, तो (a < b)स्पष्ट रूप से सच है, लेकिन a - bसकारात्मक बनने के लिए अतिप्रवाह होगा, इसलिए (a - b < 0)गलत है।

यदि आप x86 असेंबली कोड से परिचित हैं, तो विचार करें कि (a < b)इसे a द्वारा कार्यान्वित किया गया है jge, जो अगर SF = OF के स्टेटमेंट के बॉडी के चारों ओर स्थित है। दूसरी ओर, एसएफ = 0. जब शाखाएं, (a - b < 0)ए की तरह कार्य करेंगी jns, इसलिए, ये ओएफ = 1 होने पर अलग-अलग व्यवहार करते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.