एक बूलियन से थोड़ा तुलना करें


12

कहते हैं कि मेरे पास एक झंडे का एक सेट है, जिसे uint16_t में एन्कोड किया गया है flags। उदाहरण के लिए, AMAZING_FLAG = 0x02। अब, मेरे पास एक फंक्शन है। इस फ़ंक्शन को यह जांचने की आवश्यकता है कि क्या मैं ध्वज को बदलना चाहता हूं, क्योंकि यदि मैं ऐसा करना चाहता हूं, तो मुझे फ्लैश करने के लिए लिखना होगा। और वह महंगा है। इसलिए, मैं एक चेक चाहता हूं जो मुझे बताता है कि flags & AMAZING_FLAGक्या बराबर है doSet। यह पहला विचार है:

setAmazingFlag(bool doSet)
{
    if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
        // Really expensive thing
        // Update flags
    }
}

यदि यह कथन सहज नहीं है। मुझे लगता है कि एक बेहतर तरीका होना चाहिए, जैसे कुछ:

if ((flags & AMAZING_FLAG) != doSet){

}

लेकिन यह वास्तव में काम नहीं करता है, के trueबराबर लगता है 0x01

तो, एक बूलियन से थोड़ा तुलना करने का एक साफ तरीका है?


2
यह पूरी तरह से स्पष्ट नहीं है कि आपके तर्क की क्या आवश्यकता है। क्या यह है (flags & AMAZING_FLAG) && doSet:?
kaylum

प्रश्न स्पष्ट नहीं है। हम जांचना चाहते हैं कि 'झंडे' 0x01 हैं या नहीं। क्या आप चाहते हैं? यदि हाँ, तो हम बिटकॉइन ऑपरेटर 'और' का उपयोग कर सकते हैं।
विकास विजयन

यदि आप इसे और अधिक पठनीय कॉल सेट करना चाहते हैं। केवल जब doSet सत्य है, तो यह बेहतर है, तो फ़ंक्शन नाम बेहतर है अन्यथा आपके पास एक फ़ंक्शन है जो नाम नहीं कहता है या नहीं कर सकता है, खराब कोड पढ़ने के लिए बनाता है
एंडरसन

यदि आप इसे करने की कोशिश कर रहे हैं, तो इसे और अधिक पठनीय बनाने के लिए, बस एक फ़ंक्शन बनाएं 'फ्लैट्सनोटक्वालल ``।
Jeroen3

जवाबों:


18

किसी भी गैर-शून्य संख्या को 1 (सही) में बदलने के लिए, एक पुरानी चाल है: !दो बार (नहीं) ऑपरेटर को लागू करें ।

if (!!(flags & AMAZING_FLAG) != doSet){

4
या (bool)(flags & AMAZING_FLAG) != doSet- जो मुझे अधिक प्रत्यक्ष लगता है। (हालांकि यह सुझाव देता है कि श्री Microsoft के पास एक मुद्दा है।) इसके अलावा, ((flags & AMAZING_FLAG) != 0)संभवतः यह वही है जो इसे संकलित करता है और बिल्कुल स्पष्ट है।
क्रिस हॉल

"इसके अलावा, ((झंडे और AMAZING_FLAG)! = 0) शायद वही है जो इसे संकलित करता है और बिल्कुल स्पष्ट है"। क्या ऐसा? क्या हुआ अगर doSet सच है?
चीरोन

@ क्रिसहॉल करता है (बूल) 2 1 में परिणाम या यह अभी भी 2 है, सी में?
user253751

3
C11 मानक, 6.3.1.2 बूलियन प्रकार: "जब किसी स्केलर मान को _Bool में बदल दिया जाता है, तो परिणाम 0 होता है यदि मान 0 के बराबर होता है; अन्यथा, परिणाम 1. है"। और 6.5.4 कास्ट ऑपरेटर्स: "एक कोष्ठबद्ध प्रकार के नाम से एक अभिव्यक्ति को प्राथमिकता देना नाम प्रकार के अभिव्यक्ति के मूल्य को परिवर्तित करता है।"
क्रिस हॉल

@Cheiron, स्पष्ट होने के लिए: के रूप ((bool)(flags & AMAZING_FLAG) != doSet)में एक ही प्रभाव है (((flags & AMAZING_FLAG) != 0) != doSet)और वे दोनों शायद एक ही बात करने के लिए संकलन करेंगे। सुझाया गया (!!(flags & AMAZING_FLAG) != doSet)समकक्ष है, और मुझे लगता है कि एक ही चीज को भी संकलित किया जाएगा। यह पूरी तरह से स्वाद का मामला है जो आपको लगता है कि स्पष्ट है: (bool)कलाकारों को यह याद रखने की आवश्यकता है कि _Bool काम करने के लिए कैसे रूपांतरण और रूपांतरण होते हैं; !!कुछ अन्य मानसिक जिम्नास्टिक की आवश्यकता होती है, या आपके लिए "चाल" जानने के लिए।
क्रिस हॉल

2

आपको बिट मास्क को बूलियन स्टेटमेंट में बदलने की जरूरत है, जो C वैल्यू के बराबर है 0या 1

  • (flags & AMAZING_FLAG) != 0। सबसे आम तरीका है।

  • !!(flags & AMAZING_FLAG)। थोड़ा सामान्य, उपयोग करने के लिए भी ठीक है, लेकिन थोड़ा गुप्त।

  • (bool)(flags & AMAZING_FLAG)। C99 से आधुनिक तरीका और केवल परे।

उपरोक्त विकल्पों में से कोई भी लें, फिर अपनी बूलियन के साथ तुलना करके इसका उपयोग करें !=या ==


1

तार्किक दृष्टिकोण से, flags & AMAZING_FLAGकेवल अन्य बिट्स को चिह्नित करने वाला एक बिट ऑपरेशन है। परिणाम एक संख्यात्मक मूल्य है।

एक बूलियन मूल्य प्राप्त करने के लिए, आप एक तुलना का उपयोग करेंगे

(flags & AMAZING_FLAG) == AMAZING_FLAG

और अब इस तार्किक मान की तुलना कर सकते हैं doSet

if (((flags & AMAZING_FLAG) == AMAZING_FLAG) != doSet)

सी में, बूलियन मानों के लिए संख्याओं के निहित रूपांतरण नियमों के कारण संक्षिप्त रूप हो सकते हैं। तो आप भी लिख सकते हैं

if (!(flags & AMAZING_FLAG) == doSet)

अधिक कविता लिखने के लिए। लेकिन पूर्व संस्करण पठनीयता के मामले में बेहतर है।


1

आप doSetमूल्य के आधार पर एक मुखौटा बना सकते हैं :

#define AMAZING_FLAG_IDX 1
#define AMAZING_FLAG (1u << AMAZING_FLAG_IDX)
...

uint16_t set_mask = doSet << AMAZING_FLAG_IDX;

अब आपका चेक इस तरह दिख सकता है:

setAmazingFlag(bool doSet)
{
    const uint16_t set_mask = doSet << AMAZING_FLAG_IDX;

    if (flags & set_mask) {
        // Really expensive thing
        // Update flags
    }
}

कुछ आर्किटेक्चर पर, !!एक शाखा में संकलित किया जा सकता है और इसके द्वारा, आपकी दो शाखाएँ हो सकती हैं:

  1. द्वारा सामान्यीकरण !!(expr)
  2. से तुलना doSet

मेरे प्रस्ताव का लाभ एक गारंटीकृत एकल शाखा है।

नोट: सुनिश्चित करें कि आप 30 से अधिक के द्वारा छोड़ दिया है (पूर्णांक 32 बिट्स है) को छोड़ कर अपरिभाषित व्यवहार का परिचय नहीं देते हैं। इसे आसानी से हासिल किया जा सकता हैstatic_assert(AMAZING_FLAG_IDX < sizeof(int)*CHAR_BIT-1, "Invalid AMAZING_FLAG_IDX");


1
बूलियन अभिव्यक्ति के सभी तरीके एक शाखा में परिणाम की क्षमता रखते हैं। मैं अजीब मैनुअल ऑप्टिमाइज़ेशन ट्रिक्स लागू करने से पहले सबसे पहले जुदा हो जाऊंगा।
लुंडिन

1
@ लुंडिन, निश्चित रूप से, यही कारण है कि मैंने "कुछ आर्किटेक्चर पर" लिखा है ...
एलेक्स लोप।

1
यह ज्यादातर कंपाइलर के ऑप्टिमाइज़र के लिए एक मामला है और इतना ही नहीं आईएसए है।
लुंडिन

1
@ लुंडिन मैं असहमत हूँ। कुछ आर्किटेक्चर में आईएसए सशर्त बिट सेट / रीसेट के लिए होता है, जिसमें किसी ब्राच की आवश्यकता नहीं होती है, अन्य नहीं। godbolt.org/z/4FDzvw
एलेक्स लोप।

नोट: यदि आप ऐसा करते हैं, को परिभाषित कृपया AMAZING_FLAGके संदर्भ में AMAZING_FLAG_IDX(उदाहरण के लिए #define AMAZING_FLAG ((uint16_t)(1 << AMAZING_FLAG_IDX))) है, तो आप एक ही डेटा दो स्थानों पर ऐसी है कि एक अद्यतन किया जा सकता (से कहते हैं, के रूप में परिभाषित नहीं है 0x4करने के लिए 0x8अन्य, जबकि) ( IDXके 2) अपरिवर्तित छोड़ दिया है ।
शैडो रेंजर

-1

इस परीक्षण को करने के कई तरीके हैं:

टर्नरी ऑपरेटर महंगा कूद उत्पन्न कर सकता है:

if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0))

आप बूलियन रूपांतरण का उपयोग भी कर सकते हैं, जो कुशल हो सकता है या नहीं:

if (!!(flags & AMAZING_FLAG) != doSet)

या इसके समकक्ष विकल्प:

if (((flags & AMAZING_FLAG) != 0) != doSet)

यदि गुणनफल सस्ता है, तो आप इससे कूद सकते हैं:

if ((flags & AMAZING_FLAG) != doSet * AMAZING_FLAG)

यदि flagsअहस्ताक्षरित है और संकलक बहुत स्मार्ट है, तो नीचे का विभाजन एक साधारण बदलाव के लिए संकलित हो सकता है:

if ((flags & AMAZING_FLAG) / AMAZING_FLAG != doSet)

यदि वास्तुकला दो पूरक अंकगणित का उपयोग करता है, तो यहां एक और विकल्प है:

if ((flags & AMAZING_FLAG) != (-doSet & AMAZING_FLAG))

वैकल्पिक रूप से, एक flagsबिटफिल्ड के साथ एक संरचना के रूप में परिभाषित कर सकता है और एक पठनीय वाक्यविन्यास को बहुत सरल उपयोग कर सकता है:

if (flags.amazing_flag != doSet)

काश, यह दृष्टिकोण आमतौर पर पर आधारित होता है क्योंकि बिटफ़िल्ड के विनिर्देश बिट-स्तर कार्यान्वयन पर सटीक नियंत्रण की अनुमति नहीं देते हैं।


कोड के किसी भी टुकड़े का प्राथमिक लक्ष्य पठनीयता होना चाहिए, जो आपके अधिकांश उदाहरण नहीं हैं। जब आप वास्तव में अपने उदाहरणों को एक संकलक में लोड करते हैं तो इससे भी बड़ी समस्या उत्पन्न होती है: प्रारंभिक दो उदाहरण कार्यान्वयनों में सबसे अच्छा प्रदर्शन होता है: कम से कम कूदता है और लोड (ARM 8.2, -O) (वे समतुल्य हैं)। अन्य सभी कार्यान्वयन बदतर प्रदर्शन करते हैं। सामान्य तौर पर, किसी को फैंसी सामान (माइक्रो-ऑप्टिमाइज़ेशन) करने से बचना चाहिए, क्योंकि आप कंपाइलर के लिए यह पता लगाना मुश्किल कर देंगे कि क्या चल रहा है, जो प्रदर्शन को खराब करता है। इसलिए, -1।
शिरॉन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.