C / C ++ में हस्ताक्षरित अतिप्रवाह का पता लगाना


82

पहली नज़र में, यह प्रश्न डुप्लिकेट की तरह लग सकता है कि पूर्णांक ओवरफ़्लो का पता कैसे लगाया जाए? , हालांकि यह वास्तव में काफी अलग है।

मैंने पाया है कि एक अहस्ताक्षरित पूर्णांक अतिप्रवाह का पता लगाने के दौरान बहुत मामूली है, सी / सी ++ में एक हस्ताक्षरित अतिप्रवाह का पता लगाना वास्तव में ज्यादातर लोगों की तुलना में अधिक कठिन है।

सबसे स्पष्ट, अभी तक अनुभवहीन, इसे करने का तरीका कुछ इस तरह होगा:

int add(int lhs, int rhs)
{
 int sum = lhs + rhs;
 if ((lhs >= 0 && sum < rhs) || (lhs < 0 && sum > rhs)) {
  /* an overflow has occurred */
  abort();
 }
 return sum; 
}

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

भले ही उपरोक्त चेक कई संकलक पर काम करने की संभावना है, आप इस पर भरोसा नहीं कर सकते। वास्तव में, क्योंकि C मानक कहता है कि साइन इन किया गया पूर्णांक ओवरफ़्लो अपरिभाषित है, इसलिए कुछ कंपाइलर (जैसे GCC) ऑप्टिमाइज़ फ़्लैग सेट होने पर उपरोक्त चेक को ऑप्टिमाइज़ कर देंगे , क्योंकि कंपाइलर हस्ताक्षरित ओवरफ़्लो असंभव है। यह पूरी तरह से अतिप्रवाह की जांच करने के प्रयास को तोड़ देता है।

इसलिए, अतिप्रवाह की जांच करने का एक और संभावित तरीका होगा:

int add(int lhs, int rhs)
{
 if (lhs >= 0 && rhs >= 0) {
  if (INT_MAX - lhs <= rhs) {
   /* overflow has occurred */
   abort();
  }
 }
 else if (lhs < 0 && rhs < 0) {
  if (lhs <= INT_MIN - rhs) {
   /* overflow has occurred */
   abort();
  }
 }

 return lhs + rhs;
}

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

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

तो क्या यहां बेहतर समाधान है? कुछ जो 1 को गारंटी देता है) अपरिभाषित व्यवहार का कारण नहीं बनता है, और 2) कंपाइलर को ओवरफ्लो चेक को अनुकूलित करने का अवसर प्रदान नहीं करता है? मैं सोच रहा था कि दोनों ऑपरेंड को बिना बताए कास्टिंग करके और अपने खुद के दो-पूरक अंकगणित को रोल करके चेक करने का कोई तरीका हो सकता है, लेकिन मुझे यकीन नहीं है कि यह कैसे करना है।


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

9
@ अरुनशाह: गणना करना वास्तव में कठिन है और यह सुनिश्चित करना कि वे अतिप्रवाह नहीं करेंगे, और सामान्य मामले में साबित करना असंभव है। सामान्य अभ्यास संभव और आशा के रूप में व्यापक पूर्णांक प्रकार का उपयोग करना है।
डेविड थॉर्नले

6
@ अमरदीप: एक अशक्त सूचक को हस्ताक्षरित ओवरफ्लो के रूप में समान रूप से अपरिभाषित किया गया है। अपरिभाषित व्यवहार का अर्थ है कि, जहाँ तक मानक जाता है, कुछ भी हो सकता है। कोई यह नहीं मान सकता कि हस्ताक्षरित अतिप्रवाह के बाद सिस्टम अमान्य और अस्थिर स्थिति में नहीं होगा। ओपी ने इसका एक परिणाम बताया: यह ऑप्टिमाइज़र के लिए कोड को हटाने के लिए पूरी तरह से कानूनी है जो एक बार होने पर अतिप्रवाहित हस्ताक्षर का पता लगाता है।
डेविड थॉर्नले

16
@ अमरदीप: मैंने इस तरह के कार्यान्वयन का उल्लेख किया है। अनुकूलन झंडे सेट होने पर जीसीसी ओवरफ्लो चेक कोड को हटा देगा । तो यह मूल रूप से आपके कार्यक्रम को तोड़ देगा। यह स्पष्ट रूप से एक शून्य पॉइंटर डेरेफेरेंस से भी बदतर है, क्योंकि यह सूक्ष्म सुरक्षा दोषों में परिणाम कर सकता है, जबकि एक डेरे को स्थगित करना संभवतः आपके प्रोग्राम को एक segfault के साथ स्पष्ट रूप से बंद कर देगा।
चैनल'२

2
@ अमरदीप: मुझे निश्चित रूप से कार्यान्वयन लग रहा है, जहां संकलक सेटिंग्स के आधार पर, अतिप्रवाह एक जाल का कारण होगा। यह अच्छा होगा यदि भाषाएं किसी को यह निर्दिष्ट करने की अनुमति दें कि क्या विशेष रूप से अहस्ताक्षरित चर या मात्रा को (1) सफाई से लपेटना चाहिए, (2) गलती, या (3) जो कुछ भी सुविधाजनक है। ध्यान दें कि यदि एक चर मशीन के रजिस्टर आकार से छोटा है, तो यह आवश्यक है कि अहस्ताक्षरित मात्रा को सफाई से लपेटें ताकि इष्टतम कोड की पीढ़ी को रोका जा सके।
सुपरकैट

जवाबों:


26

घटाव के साथ आपका दृष्टिकोण सही और अच्छी तरह से परिभाषित है। एक कंपाइलर इसे अनुकूलित नहीं कर सकता है।

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

int sum(int a, int b)
{
    long long c;
    assert(LLONG_MAX>INT_MAX);
    c = (long long)a + b;
    if (c < INT_MIN || c > INT_MAX) abort();
    return c;
}

एक अच्छे संकलक को पूरे जोड़ और ifकथन को एक- intपरिवर्धित जोड़ और एक सशर्त कूद-ओवर-ओवरफ़्लो में परिवर्तित करना चाहिए और वास्तव में कभी भी बड़ा परिवर्धन नहीं करना चाहिए।

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


1
इसका उपयोग करने के इच्छुक किसी व्यक्ति के लिए, सुनिश्चित करें कि आप मेरे संपादित संस्करण को देख रहे हैं। मूल में मैं बेवकूफी long longके अलावा कलाकारों को पहले छोड़ देता था ।
R .. GitHub STOP की मदद से ICE

3
जिज्ञासा से बाहर, क्या आप इस अनुकूलन को करने के लिए एक कंपाइलर प्राप्त करने में सफल रहे हैं? कुछ कंपाइलरों के खिलाफ एक त्वरित परीक्षण ने ऐसा नहीं किया जो इसे कर सकता है।
स्टीफन कैनन

2
X86_64 पर, 32-बिट पूर्णांक का उपयोग करने के बारे में कुछ भी अक्षम नहीं है। प्रदर्शन 64-बिट के समान है। छोटे-से-मूल-शब्द-आकार के प्रकारों का उपयोग करने के लिए एक प्रेरणा यह है कि अतिप्रवाह / कैरी को सीधे-सुलभ स्थान पर होने के बाद से अतिप्रवाह स्थिति या कैरी (मनमानी-सटीक अंकगणित के लिए) को संभालने के लिए यह बहुत ही कुशल है।
R .. गिटहब स्टॉप हेल्पिंग ICE

2
@R।, @Steven: ओपी द्वारा दिया गया कोई घटाव कोड सही नहीं है, कृपया मेरा उत्तर देखें। मैं वहां एक कोड भी देता हूं जो सिर्फ दो तुलनाओं के साथ करता है। शायद कंपाइलर इससे बेहतर करेंगे।
जेन्स गुस्तेद

3
यह दृष्टिकोण असामान्य प्लेटफॉर्म में काम नहीं करता है जहां sizeof(long long) == sizeof(int)। C केवल वह निर्दिष्ट करता है sizeof(long long) >= sizeof(int)
chux -

36

नहीं, आपका दूसरा कोड सही नहीं है, लेकिन आप पास हैं: यदि आप सेट करते हैं

int half = INT_MAX/2;
int half1 = half + 1;

एक अतिरिक्त का परिणाम है INT_MAX। ( INT_MAXहमेशा एक विषम संख्या होती है)। तो यह वैध इनपुट है। लेकिन आपकी दिनचर्या में आपके पास होगा INT_MAX - half == half1और आप गर्भपात करेंगे। एक झूठी सकारात्मक।

इस त्रुटि को दोनों चेक के <बजाय डालकर मरम्मत की जा सकती है <=

लेकिन तब भी आपका कोड इष्टतम नहीं है। निम्नलिखित करना होगा:

int add(int lhs, int rhs)
{
 if (lhs >= 0) {
  if (INT_MAX - lhs < rhs) {
   /* would overflow */
   abort();
  }
 }
 else {
  if (rhs < INT_MIN - lhs) {
   /* would overflow */
   abort();
  }
 }
 return lhs + rhs;
}

यह देखने के लिए कि यह वैध है, आपको प्रतीकात्मक रूप lhsसे असमानताओं के दोनों किनारों पर जोड़ना होगा , और यह आपको बिल्कुल अंकगणितीय स्थिति प्रदान करता है कि आपका परिणाम सीमा से बाहर है।


सबसे अच्छा जवाब के लिए +1। माइनर: /* overflow will occurred */इस बात पर जोर देने का सुझाव दें कि पूरे बिंदु का पता लगाना है कि यदि कोड lhs + rhsवास्तव में योग किए बिना ओवरफ्लो होता है ।
चक्स -

16

IMHO, ओवरफ़्लो सेसिटिव C ++ कोड से निपटने का सबसे पूर्वी तरीका है SafeInt<T>। यह एक क्रॉस प्लेटफ़ॉर्म C ++ टेम्प्लेट है जो कोड plex पर होस्ट किया गया है जो आपको यहाँ सुरक्षा की गारंटी देता है।

मुझे यह उपयोग करने में बहुत सहज लगता है क्योंकि यह सामान्य संख्यात्मक opertations और अपवादों के माध्यम से अधिक और प्रवाह के तहत समान उपयोग पैटर्न प्रदान करता है।


14

Gcc केस के लिए, gcc 5.0 रिलीज़ नोट्स से हम देख सकते हैं कि यह अब __builtin_add_overflowइसके अलावा ओवरफ्लो की जाँच के लिए प्रदान करता है :

अतिप्रवाह जाँच के साथ अंकगणित के लिए अंतर्निहित कार्यों का एक नया सेट जोड़ा गया है: __builtin_add_overflow, __builtin_sub_overflow और __builtin_mul_overflow और clang भी अन्य वेरिएंट के साथ संगतता के लिए। इन बिल्डरों में दो अभिन्न तर्क होते हैं (जिनमें एक ही प्रकार की आवश्यकता नहीं होती है), तर्क अनंत सटीक हस्ताक्षरित प्रकार तक विस्तारित होते हैं, +, - या * उन पर किया जाता है, और परिणाम एक पूर्णांक चर में संग्रहीत होता है। आखिरी तर्क से। यदि संग्रहीत मान अनंत सटीक परिणाम के बराबर है, तो अंतर्निहित फ़ंक्शन झूठे हैं, अन्यथा सच है। पूर्णांक चर का प्रकार जो परिणाम को धारण करेगा, पहले दो तर्कों के प्रकारों से भिन्न हो सकता है।

उदाहरण के लिए:

__builtin_add_overflow( rhs, lhs, &result )

हम देख सकते हैं कि जीसीसी डॉक्यूमेंट बिल्ट-इन फंक्शंस में ओवरफ्लो चेकिंग के साथ अंकगणित का प्रदर्शन करने के लिए :

[...] इन अंतर्निहित कार्यों में सभी तर्क मूल्यों के लिए पूरी तरह से परिभाषित व्यवहार है।

क्लैंग भी चेक किए गए अंकगणित बिल्डिंस का एक सेट प्रदान करता है :

क्लैंग बिल्डरों का एक सेट प्रदान करता है जो सुरक्षा महत्वपूर्ण अनुप्रयोगों के लिए चेक अंकगणित को इस तरह से लागू करता है जो सी में तेज और आसानी से व्यक्त किया जा सकता है।

इस मामले में बिलिन होगा:

__builtin_sadd_overflow( rhs, lhs, &result )

इस समारोह प्रतीत होता है बहुत एक बात के अलावा उपयोगी: int result; __builtin_add_overflow(INT_MAX, 1, &result);स्पष्ट रूप से कहना नहीं है क्या में संग्रहीत किया जाता resultअतिप्रवाह पर और दुर्भाग्य से निर्दिष्ट करने पर चुप है अपरिभाषित व्यवहार करता नहीं पाए जाते हैं। निश्चित रूप से यही इरादा था - कोई यूबी। बेहतर है अगर यह निर्दिष्ट किया है।
chux -

1
@ अच्छा बिंदु, यह बताता है कि यहां परिणाम हमेशा परिभाषित होता है, मैंने अपना उत्तर अपडेट किया। यह विडंबना होगी अगर ऐसा नहीं होता।
शाफिक याघमोर

अपने नए संदर्भ दिलचस्प एक नहीं है (unsigned) long long *resultके लिए __builtin_(s/u)addll_overflow। निश्चित रूप से ये गलती में हैं। अन्य पहलुओं की सत्यता के रूप में एक आश्चर्य बनाता है। IAC, इनको देखकर अच्छा लगा __builtin_add/sub/mull_overflow()। आशा है कि वे किसी दिन सी कल्पना के लिए इसे बनाते हैं।
chux -

1
+1 यह मानक सी में आपको प्राप्त होने वाली किसी भी चीज़ की तुलना में बहुत बेहतर असेंबली उत्पन्न करता है, कम से कम आपके कंपाइलर के ऑप्टिमाइज़र पर भरोसा किए बिना कि आप क्या कर रहे हैं यह जानने के लिए। ऐसा पता लगाना चाहिए कि इस तरह के निर्माण कब उपलब्ध हैं और केवल एक मानक समाधान का उपयोग करें जब कंपाइलर एक प्रदान नहीं करता है।
एलेक्स रिंकिंग

11

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


6
+1 यह कहने का एक और तरीका है "यदि C इसे परिभाषित नहीं करेगा, तो आपको प्लेटफ़ॉर्म-विशिष्ट व्यवहार में मजबूर किया जाएगा।" विधानसभा में आसानी से ध्यान में रखी जाने वाली बहुत सी चीजें पोर्टेबिलिटी के नाम पर तिलहनों से पहाड़ बना रही हैं।
माइक डीस्मोन

5
मैंने एक सी सवाल के लिए एक asm जवाब के लिए एक downvote दिया। जैसा कि मैंने कहा है, सी में चेक लिखने के सही, पोर्टेबल तरीके हैं जो सटीक वही एसएसएम उत्पन्न करेगा जो आप हाथ से लिखेंगे। स्वाभाविक रूप से यदि आप इनका उपयोग करते हैं, तो प्रदर्शन प्रभाव समान होगा, और यह आपके द्वारा अनुशंसित C ++ सुरक्षित सामग्री की तुलना में बहुत कम प्रभाव होगा।
R .. गिटहब स्टॉप हेल्पिंग ICE

1
@ मैथ्यू: यदि आप ऐसा कोड लिख रहे हैं जो केवल एक कार्यान्वयन पर उपयोग किया जाएगा, और यह कार्यान्वयन गारंटी देता है कि कुछ काम करेगा, और आपको अच्छे पूर्णांक प्रदर्शन की आवश्यकता है, तो आप निश्चित रूप से कार्यान्वयन-विशिष्ट ट्रिक्स का उपयोग कर सकते हैं। हालांकि यह वह बात नहीं है जो ओपी पूछ रहा था।
डेविड थॉर्नले

3
C कार्यान्वयन-परिभाषित व्यवहार और अच्छे कारणों के लिए अपरिभाषित व्यवहार को अलग करता है, और भले ही UB आपके कार्यान्वयन के वर्तमान संस्करण में "काम करता है" , इसका मतलब यह नहीं है कि यह भविष्य के संस्करणों में काम करना जारी रखेगा। Gcc पर विचार करें और अतिप्रवाह व्यवहार पर हस्ताक्षर करें ...
R .. GitHub STOP HELPING ICE

2
चूँकि मैंने अपने -1 को इस दावे पर आधारित किया था कि हम समान कोड उत्पन्न करने के लिए C कोड प्राप्त कर सकते हैं, मुझे लगता है कि इसे वापस लेना तभी उचित होगा जब सभी प्रमुख कंपाइलर इस संबंध में कबाड़ हो जाएँगे ..
R .. GitHub STOP ICE

6

सबसे तेज़ संभव तरीका जीसीसी बिलिन का उपयोग करना है:

int add(int lhs, int rhs) {
    int sum;
    if (__builtin_add_overflow(lhs, rhs, &sum))
        abort();
    return sum;
}

X86 पर, GCC इसे संकलित करता है:

    mov %edi, %eax
    add %esi, %eax
    jo call_abort 
    ret
call_abort:
    call abort

जो प्रोसेसर के बिल्ट-इन ओवरफ्लो डिटेक्शन का उपयोग करता है।

यदि आप जीसीसी बिल्डरों का उपयोग करने के साथ ठीक नहीं हैं, तो अगला सबसे तेज़ तरीका साइन बिट्स पर बिट संचालन का उपयोग करना है। इसके अलावा हस्ताक्षरित अतिप्रवाह तब होता है जब:

  • दो ऑपरेंड के पास एक ही संकेत है, और
  • परिणाम में ऑपरेंड की तुलना में एक अलग संकेत होता है।

के हस्ताक्षर बिट ~(lhs ^ rhs)iff ऑपरेंड एक ही हस्ताक्षर है पर है, और के हस्ताक्षर बिट lhs ^ sumiff परिणाम ऑपरेंड तुलना में एक अलग संकेत है पर है। तो आप अनिर्धारित व्यवहार से बचने के लिए अहस्ताक्षरित रूप में जोड़ सकते हैं, और फिर साइन बिट का उपयोग कर सकते हैं ~(lhs ^ rhs) & (lhs ^ sum):

int add(int lhs, int rhs) {
    unsigned sum = (unsigned) lhs + (unsigned) rhs;
    if ((~(lhs ^ rhs) & (lhs ^ sum)) & 0x80000000)
        abort();
    return (int) sum;
}

इसमें संकलित है:

    lea (%rsi,%rdi), %eax
    xor %edi, %esi
    not %esi
    xor %eax, %edi
    test %edi, %esi
    js call_abort
    ret
call_abort:
    call abort

जो कि 32-बिट मशीन (gcc के साथ) पर 64-बिट प्रकार की ढलाई से काफी तेज है:

    push %ebx
    mov 12(%esp), %ecx
    mov 8(%esp), %eax
    mov %ecx, %ebx
    sar $31, %ebx
    clt
    add %ecx, %eax
    adc %ebx, %edx
    mov %eax, %ecx
    add $-2147483648, %ecx
    mov %edx, %ebx
    adc $0, %ebx
    cmp $0, %ebx
    ja call_abort
    pop %ebx
    ret
call_abort:
    call abort

1

आपके पास 64-बिट पूर्णांक में कनवर्ट करने और इस तरह की समान स्थितियों का परीक्षण करने के लिए बेहतर भाग्य हो सकता है। उदाहरण के लिए:

#include <stdint.h>

...

int64_t sum = (int64_t)lhs + (int64_t)rhs;
if (sum < INT_MIN || sum > INT_MAX) {
    // Overflow occurred!
}
else {
    return sum;
}

आप यह देखना चाहते हैं कि यहां साइन एक्सटेंशन कैसे काम करेगा, लेकिन मुझे लगता है कि यह सही है।


बिटवाइज़-और कास्ट को रिटर्न स्टेटमेंट से हटा दें। वे लिखित रूप में गलत हैं। बड़े हस्ताक्षरित पूर्णांक प्रकारों से छोटे लोगों में रूपांतरण पूरी तरह से अच्छी तरह से परिभाषित किया गया है जब तक कि मूल्य छोटे प्रकार में फिट बैठता है, और इसे एक स्पष्ट कलाकारों की आवश्यकता नहीं है। कोई भी कंपाइलर जो एक चेतावनी देता है और सुझाव देता है कि आप एक कास्ट जोड़ते हैं जब आप सिर्फ चेक करते हैं कि मूल्य अतिप्रवाह नहीं है एक टूटी हुई संकलक है।
R .. गिटहब स्टॉप हेल्पिंग ICE

@ आर आप सही हैं, मुझे अपनी जातियों के बारे में स्पष्ट होना पसंद है। मैं इसे शुद्धता के लिए बदलूंगा, हालांकि। भविष्य के पाठकों के लिए, वापसी लाइन पढ़ें return (int32_t)(sum & 0xffffffff);
जोनाथन

2
ध्यान दें कि यदि आप लिखते हैं sum & 0xffffffff, sumतो संक्षेप में टाइप में परिवर्तित किया जाता है unsigned int(32-बिट मानकर int) क्योंकि 0xffffffffटाइप है unsigned int। फिर बिटवाइज़ का परिणाम और ए है unsigned int, और यदि sumनकारात्मक था, तो यह समर्थित मूल्यों की सीमा के बाहर होगा int32_t। तब रूपांतरण int32_tमें कार्यान्वयन-परिभाषित व्यवहार होता है।
R .. गिटहब स्टॉप हेल्पिंग ICE

ध्यान दें कि यह ILP64 वातावरण में काम नहीं करेगा जहाँ ints 64-बिट हैं।
rtx13

1

कैसा रहेगा:

int sum(int n1, int n2)
{
  int result;
  if (n1 >= 0)
  {
    result = (n1 - INT_MAX)+n2; /* Can't overflow */
    if (result > 0) return INT_MAX; else return (result + INT_MAX);
  }
  else
  {
    result = (n1 - INT_MIN)+n2; /* Can't overflow */
    if (0 > result) return INT_MIN; else return (result + INT_MIN);
  }
}

मुझे लगता है कि किसी भी वैध INT_MINऔर INT_MAX(सममित या नहीं) के लिए काम करना चाहिए ; फ़ंक्शन को क्लिप के रूप में दिखाया गया है, लेकिन यह स्पष्ट होना चाहिए कि अन्य व्यवहार कैसे प्राप्त करें)।


अच्छा वैकल्पिक दृष्टिकोण के लिए +1 जो शायद अधिक सहज है।
R .. गिटहब स्टॉप हेल्पिंग ICE

1
मुझे लगता है कि यह - result = (n1 - INT_MAX)+n2;- अतिप्रवाह कर सकता है, अगर n1 छोटा था (कहो 0) और n2 नकारात्मक था।
डेवमैक

@davmac: हम्म ... शायद तीन मामलों को तोड़ना आवश्यक है: एक के साथ शुरू करें (n1 ^ n2) < 0, जो एक दो-पूरक मशीन पर होगा इसका मतलब है कि मूल्यों में विपरीत संकेत है और सीधे जोड़ा जा सकता है। यदि मानों में समान चिह्न है, तो ऊपर दिया गया दृष्टिकोण सुरक्षित होगा। दूसरी ओर, मैं उत्सुक हूँ अगर मानक के लेखकों को उम्मीद थी कि दो के पूरक-मूक-अतिप्रवाह हार्डवेयर के लिए कार्यान्वयन अतिप्रवाह के मामले में रेल को एक तरह से कूद देगा, जिसने तत्काल असामान्य कार्यक्रम समाप्ति को मजबूर नहीं किया, लेकिन कारण अन्य संगणनाओं का अप्रत्याशित व्यवधान।
20

0

स्पष्ट समाधान अहस्ताक्षरित में परिवर्तित करना है, अच्छी तरह से परिभाषित अहस्ताक्षरित अतिप्रवाह व्यवहार प्राप्त करने के लिए:

int add(int lhs, int rhs) 
{ 
   int sum = (unsigned)lhs + (unsigned)rhs; 
   if ((lhs >= 0 && sum < rhs) || (lhs < 0 && sum > rhs)) { 
      /* an overflow has occurred */ 
      abort(); 
   } 
   return sum;  
} 

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


आप अभी भी परिणाम को स्टोर कर रहे हैं sum, जो कि ए int। यदि परिणाम का मान (unsigned)lhs + (unsigned)rhsइससे अधिक है तो कार्यान्वयन-परिभाषित परिणाम या कार्यान्वयन-परिभाषित संकेत उठाया जा सकता है INT_MAX
R .. गिटहब स्टॉप हेल्पिंग ICE

2
@ आर: यह पूरा बिंदु है - व्यवहार को परिभाषित किया गया है, बजाय अपरिभाषित, इसलिए कार्यान्वयन को दस्तावेज करना चाहिए कि वह क्या करता है, और इसे लगातार करें। एक संकेत केवल तब उठाया जा सकता है जब कार्यान्वयन उसे दस्तावेज़ करता है, जिस स्थिति में उसे हमेशा उठाया जाना चाहिए और आप उस व्यवहार का उपयोग कर सकते हैं।
क्रिस डोड

0

दो longमूल्यों को जोड़ने के मामले में , पोर्टेबल कोड longमूल्य को निम्न और उच्च intभागों में विभाजित कर सकता है (या shortमामले में भागों में longसमान आकार है int):

static_assert(sizeof(long) == 2*sizeof(int), "");
long a, b;
int ai[2] = {int(a), int(a >> (8*sizeof(int)))};
int bi[2] = {int(b), int(b >> (8*sizeof(int))});
... use the 'long' type to add the elements of 'ai' and 'bi'

यदि किसी विशेष CPU को लक्षित करना इनलाइन असेंबली का उपयोग करना सबसे तेज़ तरीका है:

long a, b;
bool overflow;
#ifdef __amd64__
    asm (
        "addq %2, %0; seto %1"
        : "+r" (a), "=ro" (overflow)
        : "ro" (b)
    );
#else
    #error "unsupported CPU"
#endif
if(overflow) ...
// The result is stored in variable 'a'

-1

मुझे लगता है कि यह काम करता है:

int add(int lhs, int rhs) {
   volatile int sum = lhs + rhs;
   if (lhs != (sum - rhs) ) {
       /* overflow */
       //errno = ERANGE;
       abort();
   }
   return sum;
}

वाष्पशील का उपयोग करने से कंपाइलर को परीक्षण के अनुकूलन से दूर रखा जाता है क्योंकि यह सोचता है कि sumजोड़ और घटाव के बीच परिवर्तन हो सकता है।

इस कोड के लिए असेंबली x86_64 के लिए gcc 4.4.3 का उपयोग इसके अलावा, घटाव और परीक्षण करता है, हालांकि यह स्टैक पर सभी चीज़ों को संग्रहीत करता है और अनावश्यक स्टैक संचालन को। मैंने कोशिश भी की register volatile int sum =लेकिन विधानसभा वही थी।

केवल int sum =(कोई अस्थिर या रजिस्टर) के साथ एक संस्करण के लिए फ़ंक्शन ने परीक्षण नहीं किया और केवल एक leaनिर्देश का उपयोग करके अतिरिक्त किया ( leaयह लोड प्रभावी पता है और अक्सर झंडे रजिस्टर को छूने के बिना इसके अलावा करने के लिए उपयोग किया जाता है)।

आपका संस्करण बड़ा कोड है और बहुत अधिक कूदता है, लेकिन मुझे नहीं पता कि कौन बेहतर होगा ।


4
-1 volatileअपरिभाषित व्यवहार मुखौटा के दुरुपयोग के लिए। यदि यह "काम करता है", तो आप अभी भी "भाग्यशाली हो रहे हैं"।
R .. GitHub STOP हेल्पिंग ICE

@ आर: यदि यह काम नहीं करता है तो कंपाइलर volatileसही तरीके से लागू नहीं हो रहा है। मैं पहले से ही पूछे गए सवाल पर एक बहुत ही आम समस्या का एक सरल समाधान था।
14

हालांकि, यह विफल हो सकता है, हालांकि, एक ऐसी प्रणाली होगी जिसका संख्यात्मक प्रतिनिधित्व पूर्णांक के लिए अतिप्रवाह पर कम मूल्यों को लपेटता है।
nategoose

उस अंतिम टिप्पणी में एक "नहीं" या "नहीं" होना चाहिए।
21

@nategoose, आपका दावा है कि "अगर यह कंपाइलर काम नहीं करता है तो अस्थिरता को सही तरीके से लागू नहीं करता है" गलत है। एक बात के लिए, दो पूरक अंकगणित में, यह हमेशा सही होगा कि ओवरफ्लो होने पर भी lhs = sum - rhs। यहां तक ​​कि अगर यह मामला नहीं था, और यद्यपि यह विशेष उदाहरण थोड़ा विपरीत है, तो संकलक उदाहरण के लिए कोड उत्पन्न कर सकता है जो अतिरिक्त प्रदर्शन करता है, परिणाम मान को संग्रहीत करता है, मान को दूसरे रजिस्टर में वापस पढ़ता है, संग्रहीत मान की तुलना रीड के साथ करता है मूल्य और नोटिस कि वे समान हैं और इसलिए मान लें कि कोई अतिप्रवाह नहीं हुआ है।
davmac

-1

मेरे द्वारा, सबसे सरल जाँच ऑपरेंड के संकेत और परिणामों की जाँच होगी।

आइए राशि की जांच करें: अतिप्रवाह दोनों दिशाओं में हो सकता है, + या -, जब दोनों ऑपरेंड पर एक ही संकेत हो। और, मोटे तौर पर, अतिप्रवाह तब होगा जब परिणाम का संकेत ऑपरेंड के संकेत के समान नहीं होगा।

तो, इस तरह एक चेक पर्याप्त होगा:

int a, b, sum;
sum = a + b;
if  (((a ^ ~b) & (a ^ sum)) & 0x80000000)
    detect_oveflow();

संपादित करें: जैसा कि निल्स ने सुझाव दिया है, यह सही ifस्थिति है:

((((unsigned int)a ^ ~(unsigned int)b) & ((unsigned int)a ^ (unsigned int)sum)) & 0x80000000)

और जब से निर्देश

add eax, ebx 

अपरिभाषित व्यवहार की ओर जाता है? Intel x86 इंस्ट्रक्शन सेट रिफ़रेंस में ऐसी कोई बात नहीं है।


2
आप यहाँ बिंदु याद कर रहे हैं। आपके कोड की दूसरी पंक्ति sum = a + bअपरिभाषित व्यवहार प्राप्त कर सकती है।
Channel72

यदि आप अपने टेस्ट के अलावा
बिना अंक के

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

@ निल्स हाँ, मैं ऐसा करना चाहता था, लेकिन मुझे लगा कि चार (usngined int)एस इसे और अधिक अपठनीय बना देंगे। (आप जानते हैं, आप पहले इसे पढ़ते हैं, और इसे केवल तभी आज़माएँ जब आपको यह पसंद आए)।
रसलिक

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