कार्यान्वयन-परिभाषित व्यवहार से बचने में कुशल अहस्ताक्षरित कास्ट


93

मैं एक फ़ंक्शन को परिभाषित करना चाहता हूं जो unsigned intतर्क के रूप में लेता है और तर्क के लिए एक intअनुरूप मॉड्यूल UINT_MAX + 1 देता है।

पहला प्रयास इस तरह दिख सकता है:

int unsigned_to_signed(unsigned n)
{
    return static_cast<int>(n);
}

लेकिन जैसा कि किसी भी भाषा के वकील को पता है, INT_MAX से बड़े मूल्यों के लिए हस्ताक्षरित अहस्ताक्षरित से कास्टिंग कार्यान्वयन-परिभाषित है।

मैं इसे इस तरह लागू करना चाहता हूं कि (ए) यह केवल कल्पना के आधार पर व्यवहार पर निर्भर करता है; और (बी) यह किसी भी आधुनिक मशीन पर नो-ऑप में संकलित करता है और संकलक का अनुकूलन करता है।

जैसा कि विचित्र मशीनों के लिए ... यदि कोई हस्ताक्षरित int conuuent modulo UINT_MAX + 1 बिना हस्ताक्षरित int के नहीं है, तो मान लीजिए कि मैं एक अपवाद फेंकना चाहता हूं। यदि एक से अधिक है (मुझे यकीन नहीं है कि यह संभव है), मान लें कि मैं सबसे बड़ा चाहता हूं।

ठीक है, दूसरा प्रयास:

int unsigned_to_signed(unsigned n)
{
    int int_n = static_cast<int>(n);

    if (n == static_cast<unsigned>(int_n))
        return int_n;

    // else do something long and complicated
}

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

अब, यह दूसरा प्रयास जो मैं चाहता हूं, उसके बहुत करीब है। यद्यपि intकुछ इनपुट के लिए कलाकारों को कार्यान्वयन-परिभाषित किया जाता है, फिर unsignedभी मानक मॉडुलो UINT_MAX + 1 को संरक्षित करने के लिए मानक द्वारा गारंटी दी जाती है। तो सशर्त ठीक वही जांचता है जो मैं चाहता हूं, और यह किसी भी प्रणाली पर कुछ भी संकलित करेगा, जिसकी मुझे मुठभेड़ की संभावना है।

हालाँकि ... मैं अभी भी intपहली जाँच के बिना कास्टिंग कर रहा हूँ कि क्या यह कार्यान्वयन-परिभाषित व्यवहार को लागू करेगा। 2050 में कुछ काल्पनिक प्रणाली पर यह कर सकता था कि कौन जानता है-क्या। तो चलिए बताते हैं कि मैं इससे बचना चाहता हूं।

प्रश्न: मेरा "तीसरा प्रयास" कैसा दिखना चाहिए?

पुनरावृत्ति करने के लिए, मैं चाहता हूं:

  • अहस्ताक्षरित int से हस्ताक्षरित int कास्ट करें
  • मान mod UINT_MAX + 1 को संरक्षित करें
  • केवल मानक-अनिवार्य व्यवहार लागू करें
  • संकलक के अनुकूलन के साथ एक विशिष्ट दो-पूरक मशीन पर एक नो-ऑप में संकलित करें

[अपडेट करें]

मुझे यह दिखाने के लिए एक उदाहरण दें कि यह एक तुच्छ प्रश्न क्यों नहीं है।

निम्नलिखित गुणों के साथ एक काल्पनिक C ++ कार्यान्वयन पर विचार करें:

  • sizeof(int) बराबर ४
  • sizeof(unsigned) बराबर ४
  • INT_MAX बराबर 32767
  • INT_MINबराबर -2 32 + 32768
  • UINT_MAXबराबर 2 32 - 1
  • अंकगणित intमोडुलो 2 32 (सीमा के INT_MINमाध्यम से INT_MAX) में है
  • std::numeric_limits<int>::is_modulo सच हैं
  • nInt के लिए अहस्ताक्षरित कास्टिंग 0 <= n <= 32767 के लिए मान को संरक्षित करता है और अन्यथा शून्य देता है

इस काल्पनिक कार्यान्वयन पर, intप्रत्येक unsignedमान के लिए एक मूल्य अनुरूप (mod UINT_MAX + 1) है । तो मेरा सवाल अच्छी तरह से परिभाषित किया जाएगा।

मेरा दावा है कि यह काल्पनिक C ++ कार्यान्वयन पूरी तरह से C ++ 98, C ++ 03 और C ++ 11 विनिर्देशों के अनुरूप है। मैं मानता हूं कि मैंने उन सभी के प्रत्येक शब्द को याद नहीं किया है ... लेकिन मेरा मानना ​​है कि मैंने संबंधित अनुभागों को ध्यान से पढ़ा है। इसलिए यदि आप चाहते हैं कि मैं आपके उत्तर को स्वीकार करूं, तो आपको या तो (क) एक युक्ति का हवाला देना चाहिए जो इस काल्पनिक कार्यान्वयन को नियंत्रित करता है या (ख) इसे सही ढंग से संभालता है।

वास्तव में, एक सही उत्तर को मानक द्वारा अनुमत प्रत्येक काल्पनिक कार्यान्वयन को संभालना चाहिए । यह परिभाषा के अनुसार "केवल मानक-अनिवार्य व्यवहार को लागू करना" है।

संयोग से, ध्यान दें कि std::numeric_limits<int>::is_moduloकई कारणों से यहाँ बिल्कुल बेकार है। एक बात के लिए, यह तब trueभी हो सकता है जब बिना हस्ताक्षरित जातियां बड़े अहस्ताक्षरित मूल्यों के लिए काम न करें। दूसरे के लिए, यह किसी के trueपूरक या संकेत-परिमाण प्रणाली पर भी हो सकता है , अगर अंकगणित केवल संपूर्ण पूर्णांक सीमा को मापता है। और इसी तरह। यदि आपका उत्तर निर्भर करता है is_modulo, तो यह गलत है।

[अपडेट २]

hvd के उत्तर ने मुझे कुछ सिखाया: पूर्णांक के लिए मेरा काल्पनिक C ++ कार्यान्वयन आधुनिक सी द्वारा अनुमति नहीं है । C99 और C11 मानक हस्ताक्षरित पूर्णांक के प्रतिनिधित्व के बारे में बहुत विशिष्ट हैं; वास्तव में, वे केवल दो-पूरक, लोगों के पूरक, और साइन-परिमाण (खंड 6.2.6.2 पैरा (2);) की अनुमति देते हैं)।

लेकिन सी ++ सी नहीं है। जैसा कि यह पता चला है, यह तथ्य मेरे सवाल के बहुत दिल में है।

मूल C ++ 98 मानक बहुत पुराने C89 पर आधारित था, जो कहता है (अनुभाग 3.1.2.5):

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

C89 का कहना है कि केवल एक साइन बिट या केवल दो-पूरक / लोगों-पूरक / साइन-परिमाण की अनुमति देने के बारे में कुछ नहीं है।

C ++ 98 मानक ने इस भाषा को लगभग शब्दशः (धारा 3.9.1 पैराग्राफ (3)) को अपनाया:

हस्ताक्षर किए गए पूर्णांक प्रकारों में से प्रत्येक के लिए, एक समान (लेकिन अलग) अहस्ताक्षरित पूर्णांक प्रकार मौजूद होता है : " unsigned char", " unsigned short int", " unsigned int", और " unsigned long int", जिनमें से प्रत्येक में समान मात्रा में भंडारण होता है और समान संरेखण आवश्यकताएं होती हैं (3.9 ) संबंधित हस्ताक्षरित पूर्णांक प्रकार के रूप में; वह है, प्रत्येक हस्ताक्षरित पूर्णांक प्रकार का एक ही वस्तु प्रतिनिधित्व है जो इसके संबंधित अहस्ताक्षरित पूर्णांक प्रकार के समान है। एक हस्ताक्षरित पूर्णांक प्रकार के nonnegative मानों की सीमा संबंधित अहस्ताक्षरित पूर्णांक प्रकार की एक व्यवस्था है, और प्रत्येक संबंधित हस्ताक्षरित / अहस्ताक्षरित प्रकार का मूल्य प्रतिनिधित्व समान होगा।

C ++ 03 मानक अनिवार्य रूप से समान भाषा का उपयोग करता है, जैसा कि C ++ 11 करता है।

कोई मानक C ++ युक्ति किसी भी C युक्ति में अपने हस्ताक्षरित पूर्णांक निरूपणों में बाधा नहीं डालती, जहाँ तक मैं बता सकता हूँ। और एक भी संकेत बिट या किसी भी तरह का कुछ भी अनिवार्य नहीं है। सभी का कहना है कि गैर-नकारात्मक हस्ताक्षरित पूर्णांक, संबंधित अहस्ताक्षरित की एक व्यवस्था होनी चाहिए।

तो, फिर से मेरा दावा है कि INT_MAX = 32767 INT_MIN = -2 32 +32768 के साथ अनुमति है। यदि आपका उत्तर अन्यथा माना जाता है, तो यह गलत है जब तक कि आप C ++ मानक का हवाला नहीं देते, मुझे गलत साबित करता है।


@SteveJessop: वास्तव में, मैंने कहा था कि मैं वास्तव में उस मामले में क्या चाहता हूं: "यदि कोई हस्ताक्षरित int congruent modulo UINT_MAX + 1 बिना हस्ताक्षरित int के लिए है, तो बताएं कि मैं एक अपवाद फेंकना चाहता हूं।" यही है, मैं चाहता हूं कि "सही" हस्ताक्षरित इंट प्रदान किया जाए क्योंकि यह मौजूद है। यदि यह मौजूद नहीं है - जैसे कि गद्दी बिट्स या लोगों के पूरक प्रतिनिधित्व के मामले में हो सकता है - मैं यह पता लगाना चाहता हूं कि इसे कलाकारों के उस विशेष आह्वान के लिए संभालना चाहिए।
निमो

क्षमा करें, निश्चित नहीं कि मैं कैसे चूक गया।
स्टीव जेसोप

Btw, मुझे लगता है कि आपके काल्पनिक ट्रिकी कार्यान्वयन intमें इसे दर्शाने के लिए कम से कम 33 बिट्स की जरूरत है। मुझे पता है कि यह केवल एक फुटनोट है, इसलिए आप इसे गैर-मानक मान सकते हैं, लेकिन मुझे लगता है कि सी ++ 11 में फ़ुटनोट 49 का इरादा सच है (क्योंकि यह मानक में प्रयुक्त एक शब्द की परिभाषा है) और यह विरोधाभास नहीं करता है कुछ भी स्पष्ट रूप से प्रामाणिक पाठ में कहा गया है। तो सभी नकारात्मक मूल्यों को एक बिट पैटर्न द्वारा दर्शाया जाना चाहिए जिसमें उच्चतम बिट सेट है, और इसलिए आप 2^32 - 3276832 बिट्स में उन्हें रटना नहीं कर सकते । ऐसा नहीं है कि आपका तर्क किसी भी तरह से आकार पर निर्भर करता है int
स्टीव जेसोप

और hvd के उत्तर में आपके संपादन के बारे में, मुझे लगता है कि आपने नोट 49 की गलत व्याख्या की है। आप कहते हैं कि साइन-मैग्नीट्यूड निषिद्ध है, लेकिन यह नहीं है। आपने इसे इस रूप में पढ़ा है: "क्रमिक बिट्स द्वारा दर्शाए गए मान योगात्मक हैं, 1 से शुरू होते हैं, और (2 की क्रमिक अभिन्न शक्ति से गुणा किया जाता है, शायद उच्चतम स्थिति के साथ बिट को छोड़कर)"। मेरा मानना ​​है कि इसे पढ़ा जाना चाहिए, "क्रमिक बिट्स द्वारा दर्शाए गए मूल्य (योगात्मक हैं, 1 से शुरू होते हैं, और 2 की क्रमिक अभिन्न शक्ति से गुणा किए जाते हैं), शायद उच्चतम स्थिति के साथ बिट को छोड़कर।" यदि उच्च बिट सेट है, तो सभी दांव बंद हैं।
स्टीव जेसप

@SteveJessop: आपकी व्याख्या सही हो सकती है। यदि ऐसा है, तो यह मेरे काल्पनिक नियम को खारिज करता है ... लेकिन यह वास्तव में संभावनाओं की एक विशाल संख्या का भी परिचय देता है, जिससे इस प्रश्न का उत्तर देना बेहद कठिन हो जाता है। यह वास्तव में मेरे लिए एक बग की तरह लग रहा है। (जाहिर है, सी समिति ने ऐसा सोचा था और इसे C99 में पूरी तरह से तय किया था। मुझे आश्चर्य है कि C ++ 11 ने अपना दृष्टिकोण क्यों नहीं अपनाया?)
निमो

जवाबों:


70

User71404 के उत्तर पर विस्तार:

int f(unsigned x)
{
    if (x <= INT_MAX)
        return static_cast<int>(x);

    if (x >= INT_MIN)
        return static_cast<int>(x - INT_MIN) + INT_MIN;

    throw x; // Or whatever else you like
}

यदि x >= INT_MIN(पदोन्नति नियमों को ध्यान में रखें, INT_MINपरिवर्तित हो जाता है unsigned), तो x - INT_MIN <= INT_MAX, इसलिए इसका कोई अतिप्रवाह नहीं होगा।

यदि यह स्पष्ट नहीं है, x >= -4uतो "यदि , तो " दावे पर एक नज़र डालें x + 4 <= 3, और ध्यान रखें कि INT_MAX-INT_MIN - 1 के कम से कम गणितीय मूल्य के बराबर होगा।

सबसे आम सिस्टम पर, जहां !(x <= INT_MAX)तात्पर्य है x >= INT_MIN, दूसरा चेक निकालने के लिए ऑप्टिमाइज़र सक्षम होना चाहिए (और मेरे सिस्टम पर, सक्षम है), यह निर्धारित करें कि दो returnबयानों को एक ही कोड में संकलित किया जा सकता है, और पहले चेक को भी हटा दें। निर्मित विधानसभा सूची:

__Z1fj:
LFB6:
    .cfi_startproc
    movl    4(%esp), %eax
    ret
    .cfi_endproc

आपके प्रश्न में काल्पनिक कार्यान्वयन:

  • INT_MAX 32767 के बराबर है
  • INT_MIN बराबर -2 32 + 32768

संभव नहीं है, इसलिए विशेष विचार की आवश्यकता नहीं है। या INT_MINतो -INT_MAX, या के बराबर होगा -INT_MAX - 1। यह पूर्णांक प्रकारों (6.2.6.2) के सी प्रतिनिधित्व से आता है, जिसके लिए nबिट्स को बिट बिट्स की आवश्यकता होती है , एक बिट को एक साइन बिट होने की आवश्यकता होती है, और केवल एक एकल ट्रैप प्रतिनिधित्व की अनुमति देता है (पैडिंग बिट्स की वजह से अमान्य नहीं हैं जो प्रतिनिधित्व सहित), अर्थात् नकारात्मक शून्य / का प्रतिनिधित्व करेगा -INT_MAX - 1। C ++ किसी भी पूर्णांक अभ्यावेदन की अनुमति नहीं देता है जो C अनुमति देता है।

अपडेट : Microsoft का कंपाइलर जाहिरा तौर पर इस बात को नोटिस नहीं करता हैx > 10औरx >= 11उसी चीज का परीक्षण करता है। यह केवल वांछित कोड उत्पन्न करता है यदिx >= INT_MINइसके साथ प्रतिस्थापित किया जाता हैx > INT_MIN - 1u, जिसे यहx <= INT_MAX(इस प्लेटफ़ॉर्म पर)के नकार के रूप में पता लगा सकता है।

[प्रश्नकर्ता (निमो) से अपडेट, नीचे हमारी चर्चा पर विस्तार से]

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

आइए C ++ 11, खंड 18.3.3 के साथ शुरू करें:

तालिका 31 शीर्ष लेख का वर्णन करती है <climits>

...

सामग्री मानक सी लाइब्रेरी हेडर के समान हैं <limits.h>

यहाँ, "मानक C" का अर्थ है C99, जिसका विनिर्देशन हस्ताक्षरित पूर्णांकों के प्रतिनिधित्व को गंभीर रूप से बाधित करता है। वे केवल अहस्ताक्षरित पूर्णांक की तरह हैं, लेकिन एक बिट "साइन" और शून्य या अधिक बिट्स "पेडिंग" को समर्पित है। पैडिंग बिट्स पूर्णांक के मान में योगदान नहीं करते हैं, और साइन बिट केवल ट्वोज़-पूरक, लोगों-पूरक, या साइन-परिमाण के रूप में योगदान देता है।

चूंकि C ++ 11 <climits>C99 से मैक्रोज़ को विरासत में मिला है, इसलिए INT_MIN -INT_MAX या -INT_MAX-1 है, और hvd का कोड काम करने की गारंटी है। (ध्यान दें, पैडिंग के कारण, INT_MAX UINT_MAX / 2 की तुलना में बहुत कम हो सकता है ... लेकिन हस्ताक्षर किए गए तरीके-> अहस्ताक्षरित कलाकारों के काम के लिए धन्यवाद, यह उत्तर उस ठीक को संभालता है।)

C ++ 03 / C ++ 98 पेचीदा है। यह <climits>"स्टैंडर्ड सी" से विरासत में एक ही शब्द का उपयोग करता है , लेकिन अब "मानक सी" का अर्थ है C89 / C90।

ये सभी - C ++ 98, C ++ 03, C89 / C90 - मेरे प्रश्न में दिए गए शब्द हैं, लेकिन इसमें यह भी शामिल है (C ++ 03 खंड 3.9.1 पैराग्राफ 7):

अभिन्न प्रकार के अभ्यावेदन शुद्ध द्विआधारी संख्या प्रणाली के उपयोग द्वारा मूल्यों को परिभाषित करेंगे। (44) [ उदाहरण : यह अंतर्राष्ट्रीय मानक 2 के पूरक, 1 के पूरक और अभिन्न प्रकारों के लिए हस्ताक्षरित परिमाण अभ्यावेदन की अनुमति देता है।]

फुटनोट (44) "शुद्ध बाइनरी न्यूनीकरण प्रणाली" को परिभाषित करता है:

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

इस शब्द के बारे में जो दिलचस्प है वह यह है कि यह अपने आप में विरोधाभास है, क्योंकि "शुद्ध द्विआधारी संख्यात्मक प्रणाली" की परिभाषा एक संकेत / परिमाण प्रतिनिधित्व की अनुमति नहीं देती है! यह उच्च बिट की अनुमति देता है, कहते हैं, मान -2 n-1 (twos पूरक) या - (2 n-1 -1) (पूरक हैं)। लेकिन उच्च बिट के लिए कोई मूल्य नहीं है जो साइन / परिमाण में परिणाम करता है।

वैसे भी, मेरी "काल्पनिक कार्यान्वयन" इस परिभाषा के तहत "शुद्ध बाइनरी" के रूप में योग्य नहीं है, इसलिए इसे खारिज कर दिया गया है।

हालांकि, यह तथ्य कि उच्च बिट विशेष साधन है हम कल्पना कर सकते हैं कि यह किसी भी मूल्य पर योगदान कर सकता है: एक छोटा सा सकारात्मक मूल्य, विशाल सकारात्मक मूल्य, छोटा नकारात्मक मूल्य या विशाल नकारात्मक मूल्य। (यदि साइन बिट योगदान कर सकता है - (2 एन -1 -1), क्यों नहीं - (2 एन -1 -1) आदि?)

तो, आइए एक हस्ताक्षरित पूर्णांक प्रतिनिधित्व की कल्पना करें जो "साइन" बिट के लिए एक निराला मूल्य प्रदान करता है।

साइन बिट के लिए एक छोटा सा सकारात्मक मूल्य int(संभवतः जितना बड़ा unsigned) के लिए एक सकारात्मक सीमा में परिणाम होगा , और hvd का कोड संभालता है जो कि ठीक है।

साइन बिट के लिए एक विशाल सकारात्मक मूल्य के परिणामस्वरूप intअधिकतम अधिकतम बड़ा होगा unsigned, जो निषिद्ध है।

साइन बिट के लिए एक बड़ा नकारात्मक मूल्य intमानों की एक गैर-सन्निहित सीमा का प्रतिनिधित्व करने और संबंधित नियमों में अन्य शब्दों के परिणामस्वरूप होगा ।

अंत में, कैसे एक साइन बिट के बारे में जो एक छोटी नकारात्मक मात्रा में योगदान देता है? क्या हमारे पास "साइन बिट" योगदान में 1 हो सकता है, कहते हैं, -37 इंट के मूल्य पर? तो फिर INT_MAX होगा (कहते हैं) 2 31 -1 और INT_MIN -37 होगा?

इसके परिणामस्वरूप कुछ संख्याएँ होंगी जिनमें दो अभ्यावेदन होते हैं ... लेकिन उनका-परिशिष्ट शून्य को दो अभ्यावेदन देता है, और "उदाहरण" के अनुसार इसकी अनुमति है। कहीं भी यह अनुमान नहीं है कि शून्य एकमात्र पूर्णांक है जिसमें दो प्रतिनिधित्व हो सकते हैं। इसलिए मुझे लगता है कि यह नया काल्पनिक अनुमान है।

वास्तव में, "डाउन -INT_MAX-1बिट" के लिए मान के रूप में -1 से नीचे कोई भी नकारात्मक मान अनुमेय प्रतीत होता है, लेकिन कुछ भी छोटा नहीं है (ऐसा नहीं है कि सीमा गैर-सन्निहित हो)। दूसरे शब्दों में, -1 INT_MINसे कुछ भी हो सकता है -INT_MAX-1

अब, क्या लगता है? कार्यान्वयन-परिभाषित व्यवहार से बचने के लिए hvd के कोड में दूसरी कास्ट के लिए, हमें बस x - (unsigned)INT_MINकम या अधिक की आवश्यकता है INT_MAX। हमने सिर्फ दिखाया INT_MINकम से कम -INT_MAX-1। जाहिर xहै, सबसे अधिक है UINT_MAX। अहस्ताक्षरित करने के लिए एक ऋणात्मक संख्या कास्टिंग करना जोड़ने के समान है UINT_MAX+1। इसे एक साथ रखें:

x - (unsigned)INT_MIN <= INT_MAX

यदि और केवल यदि

UINT_MAX - (INT_MIN + UINT_MAX + 1) <= INT_MAX
-INT_MIN-1 <= INT_MAX
-INT_MIN <= INT_MAX+1
INT_MIN >= -INT_MAX-1

यह वही है जो हमने अभी दिखाया था, इसलिए इस विकृत मामले में भी, कोड वास्तव में काम करता है।

यह सभी संभावनाओं को समाप्त करता है, इस प्रकार इस अत्यंत शैक्षणिक अभ्यास को समाप्त करता है।

निचला रेखा: C89 / C90 में हस्ताक्षरित पूर्णांकों के लिए कुछ गंभीरता से निर्दिष्ट-निर्दिष्ट व्यवहार है जो C ++ 98 / ++ ++ 03 द्वारा विरासत में मिला है। यह C99 में तय किया गया है, और C ++ 11 अप्रत्यक्ष रूप <limits.h>से C99 से शामिल करके फिक्स को इनहेरिट करता है । लेकिन यहां तक ​​कि C ++ 11 आत्म-विरोधाभासी "शुद्ध द्विआधारी प्रतिनिधित्व" शब्दों को बनाए रखता है ...


प्रश्न अपडेट किया गया। मैं दूसरों को हतोत्साहित करने के लिए (अब के लिए) इस जवाब को वोट कर रहा हूं ... मैं बाद में वोट नहीं डालूंगा क्योंकि जवाब दिलचस्प है। (सी के लिए सही, लेकिन सी ++ के लिए गलत। मुझे लगता है।)
नेमो

@ नीमो इस मामले में C मानक C ++ पर लागू होता है; बहुत कम से कम, मान <limits.h>C C मानक में उसी अर्थ के रूप में C ++ मानक में परिभाषित किए गए हैं, इसलिए C की सभी आवश्यकताओं के लिए INT_MINऔर INT_MAXC ++ में इनहेरिट की गई हैं। आप सही हैं कि C ++ 03 C90 को संदर्भित करता है, और C90 अनुमत पूर्णांक अभ्यावेदन के बारे में अस्पष्ट है, लेकिन C99 परिवर्तन (कम से कम <limits.h>C ++ 11 के माध्यम से विरासत में , उम्मीद है कि अधिक सरल तरीके से भी) इसे सीमित करने के लिए उन तीनों में से एक था जो मौजूदा अभ्यास को संहिताबद्ध करता था: कोई अन्य कार्यान्वयन मौजूद नहीं था।

मैं सहमत हूं कि INT_MINआदि का अर्थ सी से विरासत में मिला है लेकिन इसका मतलब यह नहीं है कि मूल्य हैं। (वास्तव में, वे कैसे कर सकते हैं, क्योंकि प्रत्येक कार्यान्वयन अलग है?) INT_MIN1 के भीतर आपकी निष्कासन इस -INT_MAXबात पर निर्भर करती है कि बस किसी भी सी ++ युक्ति में प्रकट नहीं होता है। इसलिए जबकि C ++ मैक्रोज़ के अर्थ अर्थ को इनहेरिट करता है, कल्पना आपके अनुमान का समर्थन करने वाले शब्द को (या विरासत) प्रदान नहीं करती है। ऐसा प्रतीत होता है कि यह C ++ स्पेसीट में एक ऐसा ओवरसाइट है जो पूरी तरह से कंफर्टेबल कुशल अहस्ताक्षरित कास्ट को रोकता है।
निमो

@ नीमो यदि आप (शायद सही तरीके से) यह दावा करते हैं कि C ++ अन्य अभ्यावेदन की अनुमति देता है, तो इस तरह के कार्यान्वयन पर, मेरा दावा है कि न्यूनतम प्रतिनिधित्व योग्य प्रकार का होना आवश्यक INT_MIN नहीं हैint , क्योंकि जहाँ तक C का संबंध है, यदि प्रकार नहीं है की आवश्यकताओं से मेल खाता है int, सी मानक संभवतः उस कार्यान्वयन को किसी भी तरह से कवर नहीं कर सकता है, और सी ++ मानक "सी मानक क्या कहता है" के अलावा इसकी कोई परिभाषा प्रदान नहीं करता है। मैं जाँच करूँगा कि क्या कोई और अधिक स्पष्ट व्याख्या है।

7
यह शानदार है। पता नहीं कैसे मैं इस सवाल को उस समय याद किया।
को ऑर्बिट

17

यह कोड केवल व्यवहार पर निर्भर करता है, कल्पना द्वारा अनिवार्य है, इसलिए आवश्यकता (ए) आसानी से संतुष्ट है:

int unsigned_to_signed(unsigned n)
{
  int result = INT_MAX;

  if (n > INT_MAX && n < INT_MIN)
    throw runtime_error("no signed int for this number");

  for (unsigned i = INT_MAX; i != n; --i)
    --result;

  return result;
}

आवश्यकता (b) के साथ यह इतना आसान नहीं है। यह gcc 4.6.3 (-O, -O2, -O3) और क्लैंग 3.0 (-O, -O, -O2, -O3) के साथ नो-ऑप में संकलित करता है। Intel 12.1.0 इसे ऑप्टिमाइज़ करने से इनकार करता है। और मुझे विजुअल सी के बारे में कोई जानकारी नहीं है।


1
ठीक है, यह बहुत बढ़िया है। काश मैं इनाम 80:20 को विभाजित कर सकता ... मुझे संदेह है कि संकलक का तर्क जाता है: यदि लूप समाप्त नहीं होता है, तो resultओवरफ्लो होता है; पूर्णांक अतिप्रवाह अपरिभाषित है; इसलिए लूप समाप्त हो जाता है; इसलिए i == nसमाप्ति पर; इसलिए resultबराबर है n। मुझे अभी भी एचवीडी के जवाब (कम-स्मार्ट कंपाइलरों पर गैर-पैथोलॉजिकल व्यवहार के लिए) को प्राथमिकता देना है, लेकिन यह अधिक वोट के योग्य है।
निमो

1
अनसाइनड को मॉडुलो के रूप में परिभाषित किया गया है। लूप को भी समाप्त करने की गारंटी दी गई है क्योंकि nकुछ अहस्ताक्षरित मूल्य है और iअंततः हर अहस्ताक्षरित मूल्य तक पहुंचना चाहिए।
इडुप्री

7

मूल उत्तर ने समस्या को केवल unsigned=> के लिए हल किया int। क्या होगा यदि हम "कुछ अहस्ताक्षरित प्रकार" की सामान्य समस्या को उसके अनुरूप हस्ताक्षरित प्रकार से हल करना चाहते हैं? इसके अलावा, मूल उत्तर मानक के खंडों का उल्लेख करने और कुछ कोने के मामलों का विश्लेषण करने में उत्कृष्ट था, लेकिन यह वास्तव में मुझे यह महसूस करने में मदद नहीं करता था कि यह क्यों काम किया है, इसलिए यह उत्तर एक मजबूत वैचारिक आधार देने की कोशिश करेगा। यह उत्तर "क्यों" समझाने में मदद करने की कोशिश करेगा, और कोड को सरल बनाने के लिए आधुनिक C ++ सुविधाओं का उपयोग करेगा।

C ++ 20 उत्तर

P0907 के साथ समस्या को नाटकीय रूप से सरल किया गया है : हस्ताक्षर किए गए पूर्णांक दो के पूरक हैं और अंतिम शब्द P1236 है जिसे C ++ 20 मानक में वोट दिया गया था। अब, उत्तर यथासंभव सरल है:

template<std::unsigned_integral T>
constexpr auto cast_to_signed_integer(T const value) {
    return static_cast<std::make_signed_t<T>>(value);
}

बस। ए static_cast(या सी-स्टाइल कास्ट) को अंततः इस बात की गारंटी दी जाती है कि आपको इस प्रश्न के लिए आवश्यक चीज़ चाहिए, और जिस चीज़ को कई प्रोग्रामर ने सोचा था वह हमेशा किया।

C ++ 17 उत्तर

C ++ 17 में, चीजें बहुत अधिक जटिल हैं। हमें तीन संभावित पूर्णांक अभ्यावेदन (दो के पूरक, लोगों के पूरक, और संकेत-परिमाण) से निपटना होगा। यहां तक ​​कि अगर हम जानते हैं कि यह दो का पूरक होना चाहिए क्योंकि हमने संभावित मानों की श्रेणी की जांच की, हस्ताक्षरित पूर्णांक की सीमा के बाहर एक मूल्य का रूपांतरण उस हस्ताक्षरित पूर्णांक के लिए अभी भी हमें एक कार्यान्वयन-परिभाषित परिणाम देता है। हमें ट्रिक्स का उपयोग करना होगा जैसे हमने अन्य उत्तरों में देखा है।

सबसे पहले, यहाँ समस्या को उदारतापूर्वक हल करने के लिए कोड दिया गया है:

template<typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
constexpr auto cast_to_signed_integer(T const value) {
    using result = std::make_signed_t<T>;
    using result_limits = std::numeric_limits<result>;
    if constexpr (result_limits::min() + 1 != -result_limits::max()) {
        if (value == static_cast<T>(result_limits::max()) + 1) {
            throw std::runtime_error("Cannot convert the maximum possible unsigned to a signed value on this system");
        }
    }
    if (value <= result_limits::max()) {
        return static_cast<result>(value);
    } else {
        using promoted_unsigned = std::conditional_t<sizeof(T) <= sizeof(unsigned), unsigned, T>;
        using promoted_signed = std::make_signed_t<promoted_unsigned>;
        constexpr auto shift_by_window = [](auto x) {
            // static_cast to avoid conversion warning
            return x - static_cast<decltype(x)>(result_limits::max()) - 1;
        };
        return static_cast<result>(
            shift_by_window( // shift values from common range to negative range
                static_cast<promoted_signed>(
                    shift_by_window( // shift large values into common range
                        static_cast<promoted_unsigned>(value) // cast to avoid promotion to int
                    )
                )
            )
        );
    }
}

इसमें स्वीकृत उत्तर की तुलना में कुछ अधिक कास्ट हैं, और यह सुनिश्चित करने के लिए कि आपके कंपाइलर से कोई हस्ताक्षरित / अहस्ताक्षरित बेमेल चेतावनी नहीं है और पूर्णांक पदोन्नति नियमों को ठीक से संभालना है।

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

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

वैचारिक आधार: संख्या रेखा

पहला, यह windowअवधारणा क्या है ? निम्नलिखित संख्या लाइन पर विचार करें:

   |   signed   |
<.........................>
          |  unsigned  |

यह पता चला है कि दो के पूरक पूर्णांक के लिए, आप संख्या रेखा के सबसेट को विभाजित कर सकते हैं जो तीन समान आकार की श्रेणियों में या तो टाइप करके पहुंचा जा सकता है:

- => signed only
= => both
+ => unsigned only

<..-------=======+++++++..>

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

अन्य दो कानूनी पूर्णांक अभ्यावेदन, लोगों के पूरक और साइन-परिमाण, दो के पूरक पूर्णांक के रूप में एक ही मान के सभी समान हैं: सबसे नकारात्मक मूल्य। C ++ पूर्णांक प्रकारों के बारे में सब कुछ परिभाषित करता है, reinterpret_cast(और C ++ 20 std::bit_cast) को छोड़कर , प्रतिनिधित्व योग्य मूल्यों की सीमा के संदर्भ में, बिट प्रतिनिधित्व के संदर्भ में नहीं। इसका मतलब यह है कि हमारा विश्लेषण इन तीनों अभ्यावेदन में से प्रत्येक के लिए तब तक रहेगा जब तक हम कभी भी ट्रैप प्रतिनिधित्व बनाने की कोशिश नहीं करते। अहस्ताक्षरित मान जो इस अनुपलब्ध मान को मैप करेगा, वह एक दुर्भाग्यपूर्ण है: अहस्ताक्षरित मूल्यों के बीच में एक अधिकार। सौभाग्य से, हमारी पहली स्थिति जांच (संकलन के समय) है कि क्या ऐसा प्रतिनिधित्व मौजूद है, और फिर इसे रनटाइम चेक के साथ विशेष रूप से संभालता है।

पहली स्थिति उस मामले को संभालती है जहां हम =अनुभाग में हैं, जिसका अर्थ है कि हम ओवरलैपिंग क्षेत्र में हैं जहां एक में मूल्यों को बिना परिवर्तन के दूसरे में दर्शाया जा सकता है। shift_by_windowकोड में समारोह इन क्षेत्रों में से प्रत्येक के आकार के आधार पर सभी मूल्यों नीचे ले जाता है (हम अधिकतम मूल्य घटाना है तो 1 घटाना गणित अतिप्रवाह मुद्दों से बचने के लिए)। यदि हम उस क्षेत्र से बाहर हैं (हम इस +क्षेत्र में हैं), तो हमें एक खिड़की के आकार से नीचे कूदना होगा। यह हमें ओवरलैपिंग रेंज में रखता है, जिसका अर्थ है कि हम सुरक्षित रूप से बिना हस्ताक्षर किए हस्ताक्षरित में परिवर्तित कर सकते हैं क्योंकि मूल्य में कोई परिवर्तन नहीं है। हालाँकि, हम अभी तक नहीं किए गए हैं क्योंकि हमने प्रत्येक हस्ताक्षरित मूल्य में दो अहस्ताक्षरित मानों को मैप किया है। इसलिए, हमें अगली विंडो में शिफ्ट होने की जरूरत है (- क्षेत्र) ताकि हम फिर से एक अद्वितीय मानचित्रण करें।

अब, क्या यह हमें UINT_MAX + 1प्रश्न में अनुरोध के अनुसार परिणामी रूप से बधाई देता है? UINT_MAX + 1के बराबर है 2^n, जहां nमूल्य प्रतिनिधित्व में बिट्स की संख्या है। हमारे विंडो साइज़ के लिए हम जो वैल्यू इस्तेमाल करते हैं, वह बराबर है 2^(n - 1)(मानों के अनुक्रम में अंतिम इंडेक्स, आकार से कम है)। हम उस मूल्य को दो बार घटाते हैं, जिसका अर्थ है कि हम घटाते हैं 2 * 2^(n - 1)जो कि बराबर है 2^n। जोड़ना और घटाना xअंकगणित आधुनिक में एक सेशन है x, इसलिए हमने मूल मूल्य मॉड को प्रभावित नहीं किया है 2^n

पूर्णांक प्रचार को ठीक से संभालना

क्योंकि यह एक सामान्य कार्य है और सिर्फ नहीं है int और unsigned, हमें अभिन्न पदोन्नति नियमों के साथ खुद को भी चिंतित करना होगा। संभवतः दो दिलचस्प मामले हैं: एक जिसमें shortसे छोटा है intऔर एक जिसमें shortआकार के समान है int

उदाहरण: से shortछोटाint

यदि (आधुनिक प्लेटफार्मों पर आम) की shortतुलना में छोटा है, intतो हम यह भी जानते हैं कि इसमें unsigned shortफिट हो सकता है int, जिसका अर्थ है कि इस पर कोई भी ऑपरेशन वास्तव में होगा int, इसलिए हम इससे बचने के लिए स्पष्ट रूप से पदोन्नत प्रकार के लिए जाते हैं। हमारा अंतिम कथन बहुत सारगर्भित है और यह समझना आसान हो जाता है कि क्या हम वास्तविक मूल्यों में स्थानापन्न हैं। हमारे पहले दिलचस्प मामले के लिए, सामान्यता के किसी भी नुकसान के साथ हमें 16-बिट पर विचार करने देंshort और 17-बिटint (जो अभी भी नए नियमों के तहत अनुमति दी गई है, और इसका मतलब यह होगा कि उन दो पूर्णांक प्रकारों में से कम से कम कुछ पेडिंग बिट्स हैं ):

constexpr auto shift_by_window = [](auto x) {
    return x - static_cast<decltype(x)>(32767) - 1;
};
return static_cast<int16_t>(
    shift_by_window(
        static_cast<int17_t>(
            shift_by_window(
                static_cast<uint17_t>(value)
            )
        )
    )
);

सबसे बड़ी संभव 16-बिट अहस्ताक्षरित मूल्य के लिए समाधान

constexpr auto shift_by_window = [](auto x) {
    return x - static_cast<decltype(x)>(32767) - 1;
};
return int16_t(
    shift_by_window(
        int17_t(
            shift_by_window(
                uint17_t(65535)
            )
        )
    )
);

को सरल करता है

return int16_t(
    int17_t(
        uint17_t(65535) - uint17_t(32767) - 1
    ) -
    int17_t(32767) -
    1
);

को सरल करता है

return int16_t(
    int17_t(uint17_t(32767)) -
    int17_t(32767) -
    1
);

को सरल करता है

return int16_t(
    int17_t(32767) -
    int17_t(32767) -
    1
);

को सरल करता है

return int16_t(-1);

हम सबसे बड़ा संभव अहस्ताक्षरित में डाल दिया और वापस -1, सफलता!

उदाहरण: short जैसा आकारint

यदि shortसमान आकार int(आधुनिक प्लेटफार्मों पर असामान्य) है, तो अभिन्न पदोन्नति नियम थोड़ा अलग है। इस मामले में, shortको बढ़ावा देता है intऔर unsigned shortकरने के लिए बढ़ावा देता है unsigned। सौभाग्य से, हम प्रत्येक परिणाम को स्पष्ट रूप से उस प्रकार में डालते हैं, जिसमें हम गणना करना चाहते हैं, इसलिए हम कोई समस्याग्रस्त पदोन्नति नहीं करते हैं। सामान्यता के नुकसान के साथ हमें 16-बिट shortऔर 16-बिट पर विचार करने दें int:

constexpr auto shift_by_window = [](auto x) {
    return x - static_cast<decltype(x)>(32767) - 1;
};
return static_cast<int16_t>(
    shift_by_window(
        static_cast<int16_t>(
            shift_by_window(
                static_cast<uint16_t>(value)
            )
        )
    )
);

सबसे बड़ी संभव 16-बिट अहस्ताक्षरित मूल्य के लिए समाधान

auto x = int16_t(
    uint16_t(65535) - uint16_t(32767) - 1
);
return int16_t(
    x - int16_t(32767) - 1
);

को सरल करता है

return int16_t(
    int16_t(32767) - int16_t(32767) - 1
);

को सरल करता है

return int16_t(-1);

हम सबसे बड़ा संभव अहस्ताक्षरित में डाल दिया और वापस -1, सफलता!

क्या होगा अगर मैं बस के बारे में परवाह है intऔर unsignedचेतावनी के बारे में परवाह नहीं है, मूल प्रश्न की तरह?

constexpr int cast_to_signed_integer(unsigned const value) {
    using result_limits = std::numeric_limits<int>;
    if constexpr (result_limits::min() + 1 != -result_limits::max()) {
        if (value == static_cast<unsigned>(result_limits::max()) + 1) {
            throw std::runtime_error("Cannot convert the maximum possible unsigned to a signed value on this system");
        }
    }
    if (value <= result_limits::max()) {
        return static_cast<int>(value);
    } else {
        constexpr int window = result_limits::min();
        return static_cast<int>(value + window) + window;
    }
}

इसे लाइव देखें

https://godbolt.org/z/74hY81

यहाँ हम देखते हैं कि बजना, जीसीसी, और आईसीसी के लिए कोई कोड उत्पन्न castऔर cast_to_signed_integer_basicपर -O2और -O3, और MSVC में कोई कोड उत्पन्न करता है /O2तो समाधान इष्टतम है।


3

आप संकलक को स्पष्ट रूप से बता सकते हैं कि आप क्या करना चाहते हैं:

int unsigned_to_signed(unsigned n) {
  if (n > INT_MAX) {
    if (n <= UINT_MAX + INT_MIN) {
      throw "no result";
    }
    return static_cast<int>(n + INT_MIN) - (UINT_MAX + INT_MIN + 1);
  } else {
    return static_cast<int>(n);
  }
}

के gcc 4.7.2लिए x86_64-linux( g++ -O -S test.cpp) के साथ संकलन

_Z18unsigned_to_signedj:
    movl    %edi, %eax
    ret

UINT_MAXप्रकार की एक अभिव्यक्ति है unsigned int, और यह आपके static_cast<int>(n + INT_MIN) - (UINT_MAX + INT_MIN + 1)प्रकार के पूरे बनाता है । हालांकि, इसे ठीक करना संभव होना चाहिए, और मुझे उम्मीद है कि तब भी इसे संकलित किया जाएगा।

2

अगर x हमारा इनपुट है ...

तो x > INT_MAX, हम एक निरंतर लगाना चाहते हैं kऐसी है कि 0< x - k*INT_MAX<INT_MAX

यह आसान है -- unsigned int k = x / INT_MAX; । तो करने देंunsigned int x2 = x - k*INT_MAX;

अब हम डाल सकता x2करने के लिएint सुरक्षित रूप से। चलोint x3 = static_cast<int>(x2);

हम अब UINT_MAX - k * INT_MAX + 1से कुछ घटाना चाहते हैं x3, अगरk > 0

अब, 2s पूरक प्रणाली पर, तब तक, जब तक x > INT_MAXयह काम करता है:

unsigned int k = x / INT_MAX;
x -= k*INT_MAX;
int r = int(x);
r += k*INT_MAX;
r -= UINT_MAX+1;

ध्यान दें कि UINT_MAX+1C ++ गारंटी में शून्य है, int में रूपांतरण एक शून्य था, और हम घटाए गएk*INT_MAX फिर "समान मूल्य" पर वापस जोड़ा। तो एक स्वीकार्य ऑप्टिमाइज़र को उस सभी कब्रिस्तान को मिटा देने में सक्षम होना चाहिए!

वह समस्या को छोड़ता है x > INT_MAXया नहीं। ठीक है, हम 2 शाखाएँ बनाते हैं, एक साथ x > INT_MAX, और एक बिना। इसके बिना एक स्ट्रेट कास्ट करता है, जो कंपाइलर एक नॉओप को अनुकूलित करता है। के साथ एक ... अनुकूलक के बाद एक noop करता है। स्मार्ट ऑप्टिमाइज़र दोनों शाखाओं को एक ही चीज़ का एहसास कराता है, और शाखा को छोड़ देता है।

मुद्दे: अगर UINT_MAXवास्तव में बड़े रिश्तेदार हैं INT_MAX, तो ऊपर काम नहीं कर सकता है। मैं यह मानकर चल रहा हूं k*INT_MAX <= UINT_MAX+1

हम संभवत: कुछ एनम के साथ इस पर हमला कर सकते हैं

enum { divisor = UINT_MAX/INT_MAX, remainder = UINT_MAX-divisor*INT_MAX };

जो 2s और 2 पर 1 के पूरक प्रणाली पर काम करता है, मेरा मानना ​​है कि (क्या हम काम करने के लिए उस गणित के लिए गारंटी है? यह मुश्किल है ...), और इन पर आधारित तर्क है कि आसानी से गैर -2 s पूरक प्रणाली पर अनुकूलन ...

यह अपवाद मामले को भी खोलता है। यह केवल तभी संभव है जब UINT_MAX, (INT_MIN-INT_MAX) की तुलना में बहुत बड़ा है, इसलिए आप अपने अपवाद कोड को एक ब्लॉक में डाल सकते हैं, जो वास्तव में किसी भी तरह का प्रश्न पूछ सकता है, और यह आपको एक पारंपरिक प्रणाली में धीमा नहीं करेगा।

मुझे ठीक से पता नहीं है कि उस समय को सही ढंग से निपटने के लिए उन संकलित समय का निर्माण कैसे किया जाए।


UINT_MAXके सापेक्ष छोटा नहीं हो सकता है INT_MAX, क्योंकि यह गारंटी देता है कि प्रत्येक सकारात्मक हस्ताक्षरित int एक अहस्ताक्षरित int के रूप में प्रतिनिधित्व करने योग्य है। लेकिन UINT_MAX+1हर प्रणाली पर शून्य है; अहस्ताक्षरित अंकगणित हमेशा मोडुलो होता है UINT_MAX+1। अभी भी यहाँ एक व्यावहारिक दृष्टिकोण का एक कर्नेल हो सकता है ...
निमो

@Nemo बस, इस सूत्र निम्नलिखित तो मेरे संभावित स्पष्ट सवाल क्षमा: अपने बयान "है UINT_MAX+1?। हर में '03 -spec यदि हां, है एक विशेष उपधारा के तहत धन्यवाद देख जाना चाहिए स्थापित system` पर शून्य है
WhozCraig

@HozCraig: धारा 3.9.1 पैराग्राफ 4: "अनइंस्टॉल किए गए पूर्णांक, जिन्हें अहस्ताक्षरित घोषित किया गया है, अंकगणित के नियमों का पालन करेंगे 2 ^ n जहां पूर्णांक के उस विशेष आकार के मूल्य प्रतिनिधित्व में बिट्स की संख्या है", एक फुटनोट के साथ "इसका तात्पर्य यह है कि अहस्ताक्षरित अंकगणित अतिप्रवाह नहीं करता है क्योंकि परिणामी अहस्ताक्षरित पूर्णांक प्रकार द्वारा प्रतिनिधित्व नहीं किया जा सकता है क्योंकि यह संख्या उस संख्या को कम कर दी जाती है जो उस सबसे बड़े मान से अधिक होती है जिसे परिणामित अहस्ताक्षरित पूर्णांक प्रकार द्वारा दर्शाया जा सकता है।" मूल रूप से अहस्ताक्षरित आपके द्वारा अपेक्षित / अपेक्षित तरीके से काम करने के लिए निर्दिष्ट है।
निमो

@ नीमो थैंक्स। बहुत ज्यादा अधिमूल्यित।
WhozCraig

1

std::numeric_limits<int>::is_moduloएक संकलन समय स्थिर है। इसलिए आप इसका उपयोग टेम्पलेट विशेषज्ञता के लिए कर सकते हैं। समस्या हल हो गई, कम से कम अगर कंपाइलर इनलाइनिंग के साथ खेलता है।

#include <limits>
#include <stdexcept>
#include <string>

#ifdef TESTING_SF
    bool const testing_sf = true;
#else
    bool const testing_sf = false;
#endif

// C++ "extensions"
namespace cppx {
    using std::runtime_error;
    using std::string;

    inline bool hopefully( bool const c ) { return c; }
    inline bool throw_x( string const& s ) { throw runtime_error( s ); }

}  // namespace cppx

// C++ "portability perversions"
namespace cppp {
    using cppx::hopefully;
    using cppx::throw_x;
    using std::numeric_limits;

    namespace detail {
        template< bool isTwosComplement >
        int signed_from( unsigned const n )
        {
            if( n <= unsigned( numeric_limits<int>::max() ) )
            {
                return static_cast<int>( n );
            }

            unsigned const u_max = unsigned( -1 );
            unsigned const u_half = u_max/2 + 1;

            if( n == u_half )
            {
                throw_x( "signed_from: unsupported value (negative max)" );
            }

            int const i_quarter = static_cast<int>( u_half/2 );
            int const int_n1 = static_cast<int>( n - u_half );
            int const int_n2 = int_n1 - i_quarter;
            int const int_n3 = int_n2 - i_quarter;

            hopefully( n == static_cast<unsigned>( int_n3 ) )
                || throw_x( "signed_from: range error" );

            return int_n3;
        }

        template<>
        inline int signed_from<true>( unsigned const n )
        {
            return static_cast<int>( n );
        }
    }    // namespace detail

    inline int signed_from( unsigned const n )
    {
        bool const is_modulo = numeric_limits< int >::is_modulo;
        return detail::signed_from< is_modulo && !testing_sf >( n );
    }
}    // namespace cppp

#include <iostream>
using namespace std;
int main()
{
    int const x = cppp::signed_from( -42u );
    wcout << x << endl;
}


संपादित करें : गैर-मॉड्यूलर-इंट मशीनों पर संभावित जाल से बचने के लिए निश्चित कोड (केवल एक ही अस्तित्व में है, अर्थात् यूनिसे क्लीपैथ के पुरातन कॉन्फ़िगर किए गए संस्करण)। सादगी के लिए यह मान -2 एन -1 का समर्थन न करके किया जाता है, जहां nint मूल्य बिट्स की संख्या है , ऐसी मशीन पर (यानी, क्लीरापाथ पर)। व्यवहार में यह मान या तो मशीन द्वारा समर्थित नहीं होगा (यानी, साइन-एंड-परिमाण या 1 के पूरक प्रतिनिधित्व के साथ)।


1

मुझे लगता है कि इंट प्रकार कम से कम दो बाइट्स हैं, इसलिए INT_MIN और INT_MAX विभिन्न प्लेटफार्मों में बदल सकते हैं।

मौलिक प्रकार

≤climits≤ हैडर


मैं 6809 के लिए एक कंपाइलर का उपयोग करने के लिए अभिशप्त हूं, जिसे डिफ़ॉल्ट रूप से "-mint8" के साथ कॉन्फ़िगर किया गया है, जहां int 8 बिट्स है :-( (यह वेक्ट्रेक्स के लिए विकास का वातावरण है) 2 बाइट्स है, लंबे समय तक 4 बाइट्स है मुझे नहीं पता कि क्या कम है ...
ग्राहम टोल

1

मेरा पैसा मेमकी का उपयोग करने पर है। कोई भी सभ्य संकलक इसे दूर अनुकूलित करना जानता है:

#include <stdio.h>
#include <memory.h>
#include <limits.h>

static inline int unsigned_to_signed(unsigned n)
{
    int result;
    memcpy( &result, &n, sizeof(result));
    return result;
}

int main(int argc, const char * argv[])
{
    unsigned int x = UINT_MAX - 1;
    int xx = unsigned_to_signed(x);
    return xx;
}

मेरे लिए (Xcode 8.3.2, Apple LLVM 8.1, -O3), जो उत्पादन करता है:

_main:                                  ## @main
Lfunc_begin0:
    .loc    1 21 0                  ## /Users/Someone/main.c:21:0
    .cfi_startproc
## BB#0:
    pushq    %rbp
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp2:
    .cfi_def_cfa_register %rbp
    ##DEBUG_VALUE: main:argc <- %EDI
    ##DEBUG_VALUE: main:argv <- %RSI
Ltmp3:
    ##DEBUG_VALUE: main:x <- 2147483646
    ##DEBUG_VALUE: main:xx <- 2147483646
    .loc    1 24 5 prologue_end     ## /Users/Someone/main.c:24:5
    movl    $-2, %eax
    popq    %rbp
    retq
Ltmp4:
Lfunc_end0:
    .cfi_endproc

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