संतृप्त बाइट्स के लिए घटाना / जोड़ना


83

कल्पना कीजिए कि मेरे पास दो अहस्ताक्षरित बाइट्स bऔर हैं x। मैं गणना करने के लिए की जरूरत है bsubके रूप में b - xऔर baddके रूप में b + x। हालाँकि, मैं नहीं चाहता कि इन ऑपरेशनों के दौरान अंडरफ्लो / ओवरफ्लो हो। उदाहरण के लिए (छद्म कोड):

b = 3; x = 5;
bsub = b - x; // bsub must be 0, not 254

तथा

b = 250; x = 10;
badd = b + x; // badd must be 255, not 4

ऐसा करने का स्पष्ट तरीका शाखायुक्त है:

bsub = b - min(b, x);
badd = b + min(255 - b, x);

मुझे आश्चर्य है कि अगर ऐसा करने के लिए कोई बेहतर तरीके हैं, यानी कुछ हैकरी बिट जोड़तोड़ से?


13
y ^ ((x ^ y) & -(x < y))के लिए intप्रकार का मूल्यांकन करता है min(x, y)शाखाओं के बिना। यह आपके लिए अब तक के आधार पर एक अंतिम समाधान का हिस्सा बन सकता है।
बथशेबा


8
क्या यह C या C ++ प्रश्न है? कृपया एक चुनें।
फ़ूज

9
@ एलनकैम्पबेल इसे सेटरेटिंग अरिथमेटिक कहा जाता है ।
शफीक याघमौर

7
क्या आपको पोर्टेबल होने की आवश्यकता है? क्योंकि यदि आप एक विशिष्ट वास्तुकला देख रहे हैं, तो शायद एक अच्छा एकल निर्देश है। मुझे पता है कि एआरएम के पास बाइट के लिए वेक्टर जोड़ और घटाव है। X86 पर _mm_adds_epi8आंतरिक एकल निर्देश में 16 बाइट्स का एक संतृप्त जोड़ देगा।
4

जवाबों:


86

लेख शाखा सतही अंकगणित अंकगणित इसके लिए रणनीति प्रदान करता है:

उनके अतिरिक्त समाधान इस प्रकार है:

u32b sat_addu32b(u32b x, u32b y)
{
    u32b res = x + y;
    res |= -(res < x);

    return res;
}

uint8_t के लिए संशोधित:

uint8_t  sat_addu8b(uint8_t x, uint8_t y)
{
    uint8_t res = x + y;
    res |= -(res < x);

    return res;
}

और उनका घटाव समाधान है:

u32b sat_subu32b(u32b x, u32b y)
{
    u32b res = x - y;
    res &= -(res <= x);

    return res;
}

uint8_t के लिए संशोधित:

uint8_t sat_subu8b(uint8_t x, uint8_t y)
{
    uint8_t res = x - y;
    res &= -(res <= x);

    return res;
}

2
@ user1969104 यह मामला हो सकता है लेकिन जैसा कि लेख में टिप्पणी से संकेत मिलता है, कि एक शून्य से लागू करने से पहले अहस्ताक्षरित करने के लिए कास्टिंग द्वारा हल किया जाता है। व्यावहारिकता में यह संभावना नहीं है कि आपको किसी और चीज के साथ सौदा करना होगा, लेकिन दो के पूरक
शफीक याघमौर

2
यह एक अच्छा C उत्तर हो सकता है, लेकिन बहुत अच्छा C ++ उत्तर नहीं है।
यक - एडम नेवेरुमोंट

4
@Yakk यह "खराब" C ++ उत्तर क्या है? ये बुनियादी गणितीय कार्य हैं, और मैं यह नहीं देखता कि इसे केवल C या खराब C ++ के रूप में कैसे समझा जाएगा।
JPhi1618

4
@ JPhi1618 एक बेहतर C ++ उत्तर शायद template<class T>struct sat{T t;};अतिभारित ऑपरेटरों के साथ हो सकता है जो संतृप्त हैं? नामस्थानों का समुचित उपयोग। ज्यादातर चीनी।
यक्क - एडम नेवरामॉन्ट 19

6
@ यक, आह, ठीक है। मैंने इसे केवल एक न्यूनतम उदाहरण के रूप में देखा कि ओपी आवश्यकता के अनुसार अनुकूलित कर सकता है। मैं कार्यान्वयन को पूरा करने की उम्मीद नहीं करूंगा। स्पष्टीकरण देने के लिए धन्यवाद।
जेपी 1618 18

40

एक सरल विधि है कि अतिप्रवाह का पता लगाना और नीचे के अनुसार मूल्य को रीसेट करना

bsub = b - x;
if (bsub > b)
{
    bsub = 0;
}

badd = b + x;
if (badd < b)
{
    badd = 255;
}

जब जी -२ के साथ संकलित होता है तो जीसीसी ओवरफ्लो चेक को एक सशर्त असाइनमेंट में अनुकूलित कर सकता है।

मैंने मापा कि अन्य समाधानों की तुलना में कितना अनुकूलन है। मेरे पीसी पर 1000000000+ ऑपरेशंस के साथ, इस सॉल्यूशन और @ShafikYaghmour का औसत 4.2 सेकंड था, और @chux का औसत 4.8 सेकंड था। यह समाधान अधिक पठनीय भी है।


5
@ user694733 यह अनुकूलित नहीं है, यह कैरी फ़्लैग के आधार पर एक सशर्त असाइनमेंट में अनुकूलित है।
फ़ूज

2
हां user694733 सही है। इसे सशर्त असाइनमेंट में अनुकूलित किया गया है।
user1969104

यह सभी मामलों के लिए काम नहीं करेगा, उदाहरण के लिए badd: b = 155 x = 201, badd = 156 की तुलना में, और यह b से बड़ा है। आपको ऑपरेशन के आधार पर परिणाम की न्यूनतम () या अधिकतम () दो चर की तुलना करने की आवश्यकता होगी
क्रिस्टियन F

@CristianF आप 155 + 201 = 156 की गणना कैसे करते हैं? मुझे लगता है कि इसे 155 + 201 = 356% 256 = 100 होना चाहिए। मुझे नहीं लगता कि मिन (), अधिकतम () बी, एक्स मानों के किसी भी संयोजन में आवश्यक है।
user1969104

16

घटाव के लिए:

diff = (a - b)*(a >= b);

इसके अलावा:

sum = (a + b) | -(a > (255 - b))

क्रमागत उन्नति

// sum = (a + b)*(a <= (255-b)); this fails
// sum = (a + b) | -(a <= (255 - b)) falis too

@R_Kapp को धन्यवाद

@NathanOliver को धन्यवाद

यह अभ्यास केवल कोडिंग का मूल्य दर्शाता है।

sum = b + min(255 - b, a);

के लिए sumशायद (a + b) | -(a <= (255 - b))?
R_Kapp 15

आप ऐसा कर सकते हैं sum = ((a + b) | (!!((a + b) & ~0xFF) * 0xFF)) & 0xFF, sizeof(int) > sizeof(unsigned char)लेकिन यह इतना जटिल लग रहा है कि मुझे नहीं पता कि आप इसके साथ कुछ भी हासिल करेंगे (सिरदर्द के अलावा)।
user694733

@ user694733 हाँ और शायद भी (a+b+1)*(a <= (255-b)) - 1
chux -

@NathanOliver ओवरसाइट के लिए धन्यवाद - इसका पहलू यह है कि subसीमा जितनी आसान थी 0। लेकिन अन्य सीमाएं जटिलताओं को रोकती हैं और उपयोगकर्ता 2079303 टिप्पणी का पालन करती हैं
chux -

1
@ user1969104 ओपी "बेहतर" (कोड स्थान बनाम गति प्रदर्शन) पर स्पष्ट नहीं था और न ही लक्ष्य मंच और न ही संकलक। स्पीड मूल्यांकन, अन-पोस्टेड बड़ी समस्या के संदर्भ में सबसे अधिक समझ में आता है।
chux -

13

यदि आप gcc या clang (शायद कुछ अन्य भी) के हालिया पर्याप्त संस्करण का उपयोग कर रहे हैं, तो आप ओवरफ्लो का पता लगाने के लिए बिल्ट-इन का उपयोग कर सकते हैं ।

if (__builtin_add_overflow(a,b,&c))
{
  c = UINT_MAX;
}

यह सबसे अच्छा जवाब है। बिट मैजिक के बजाए कंपाइलर बिल्ट-इन का उपयोग करना न केवल तेज है, बल्कि यह स्पष्ट भी है और कोड को अधिक बनाए रखता है।
सेफलोपॉड

साभार, @erebos मैं निश्चित रूप से उन प्लेटफार्मों पर यह कोशिश करूँगा जहाँ यह उपलब्ध है।
ओवॉक

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

1
@ Cephalopod और यह पूरी तरह से गैर-क्रॉसप्ला रिकॉर्डर है, सबसे अधिक संभावना है कि यह दूसरे कंपाइलर पर भी काम नहीं करता है। 21 वीं सदी के लिए एक अच्छा समाधान नहीं है।
इला 782

1
@ Ela782 यह बिलकुल दूसरा तरीका है: बिल्ट-इन 20 वीं सदी के लिए अच्छा समाधान नहीं है। आने वाला कल आपका स्वागत करता है!
सेफेलोपॉड

3

इसके अलावा:

unsigned temp = a+b;  // temp>>8 will be 1 if overflow else 0
unsigned char c = temp | -(temp >> 8);

घटाव के लिए:

unsigned temp = a-b;  // temp>>8 will be 0xFF if neg-overflow else 0
unsigned char c = temp & ~(temp >> 8);

कोई तुलना ऑपरेटरों या गुणकों की आवश्यकता नहीं है।


3

यदि आप असेंबली या इंट्रिनिक्स का उपयोग करने के इच्छुक हैं, तो मुझे लगता है कि मेरे पास एक इष्टतम समाधान है।

घटाव के लिए:

हम निर्देश का उपयोग कर सकते हैंsbb

MSVC में हम आंतरिक फ़ंक्शन _subborrow_u64 (अन्य बिट आकारों में भी उपलब्ध) का उपयोग कर सकते हैं ।

यहाँ इसका उपयोग कैसे किया जाता है:

// *c = a - (b + borrow)
// borrow_flag is set to 1 if (a < (b + borrow))
borrow_flag = _subborrow_u64(borrow_flag, a, b, c);

यहां बताया गया है कि हम इसे आपकी स्थिति पर कैसे लागू कर सकते हैं

uint64_t sub_no_underflow(uint64_t a, uint64_t b){
    uint64_t result;
    borrow_flag = _subborrow_u64(0, a, b, &result);
    return result * !borrow_flag;
}

इसके अलावा:

हम निर्देश का उपयोग कर सकते हैंadcx

MSVC में हम आंतरिक समारोह _addcarry_u64 (अन्य बिट आकारों में भी उपलब्ध) का उपयोग कर सकते हैं ।

यहाँ इसका उपयोग कैसे किया जाता है:

// *c = a + b + carry
// carry_flag is set to 1 if there is a carry bit
carry_flag = _addcarry_u64(carry_flag, a, b, c);

यहां बताया गया है कि हम इसे आपकी स्थिति पर कैसे लागू कर सकते हैं

uint64_t add_no_overflow(uint64_t a, uint64_t b){
    uint64_t result;
    carry_flag = _addcarry_u64(0, a, b, &result);
    return !carry_flag * result - carry_flag;
}

मैं इस एक के रूप में ज्यादा के रूप में एक घटाव पसंद नहीं है, लेकिन मुझे लगता है कि यह बहुत अच्छा है।

यदि ओवरफ्लो जोड़ें carry_flag = 1,। carry_flagपैदावार नहीं , इसलिए !carry_flag * result = 0जब अतिप्रवाह होता है। और चूँकि 0 - 1बिना बिके हुए अभिन्न मान को उसके अधिकतम पर सेट करेगा, तो फंक्शन इसके अतिरिक्त परिणाम देगा यदि कोई कैरी नहीं है और कैरी होने पर चुने हुए अभिन्न मूल्य का अधिकतम रिटर्न देगा।


1
आप यह उल्लेख करना चाहते हैं कि यह उत्तर एक विशिष्ट निर्देश-सेट वास्तुकला (x86?) के लिए है और प्रत्येक लक्ष्य वास्तुकला (SPARC, MIPS, ARM, आदि) के लिए पुन: क्रियान्वित करने की आवश्यकता होगी
Toby Speight

2

इस बारे में क्या:

bsum = a + b;
bsum = (bsum < a || bsum < b) ? 255 : bsum;

bsub = a - b;
bsub = (bsub > a || bsub > b) ? 0 : bsub;

मैंने टाइपो (स्पष्ट?) टाइपो तय किया, लेकिन मुझे अभी भी नहीं लगता कि यह सही है।
बतशेबा

इसमें ब्रांचिंग भी शामिल है।
फ़ूज

मैं इस उत्तर को असेंबली में एक त्वरित प्रश्न के अनुकूलन के बिना हटा दूंगा कि टर्नरी ऑपरेटर और यदि / और कथन में क्या अंतर है?

@GRC कोई अंतर नहीं है।
15

@GRC FUZxxl सही है, लेकिन, हमेशा की तरह, अपने आप को आज़माएं। भले ही आपको असेंबली की जानकारी न हो (आप एसओ पर यहां एक सवाल कर सकते हैं, अगर आपके लिए कुछ स्पष्ट नहीं है), तो केवल लंबाई / निर्देशों को जानकर।
17

2

सभी अहस्ताक्षरित बाइट अंकगणित में किया जा सकता है

// Addition without overflow
return (b > 255 - a) ? 255 : a + b

// Subtraction without underflow
return (b > a) ? 0 : a - b;

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

2

यदि आप दो बाइट्स के साथ ऐसा करना चाहते हैं, तो संभव सबसे सरल कोड का उपयोग करें।

यदि आप इसे बीस बिलियन बाइट्स के साथ करना चाहते हैं, तो जांचें कि आपके प्रोसेसर पर वेक्टर निर्देश क्या उपलब्ध हैं और क्या उनका उपयोग किया जा सकता है। आप पा सकते हैं कि आपका प्रोसेसर एक ही निर्देश के साथ इनमें से 32 ऑपरेशन कर सकता है।


2

आप बूस्ट लाइब्रेरी इनक्यूबेटर में सुरक्षित न्यूमेरिक्स लाइब्रेरी का भी उपयोग कर सकते हैं । यह int, long, etc ... के लिए ड्रॉप-इन रिप्लेसमेंट प्रदान करता है ... जो गारंटी देता है कि आपको कभी भी एक अनपेक्षित ओवरफ़्लो, अंडरफ़्लो, आदि नहीं मिलेगा।


7
पुस्तकालय का उपयोग कैसे करें, इसका एक उदाहरण प्रदान करने से यह एक बेहतर उत्तर होगा। इसके अलावा, क्या वे एक साहसी गारंटी प्रदान करते हैं?
शाफिक याघमौर 16

पुस्तकालय में व्यापक दस्तावेज और उदाहरण हैं। लेकिन दिन का अंत उतना ही आसान है, जितना कि उपयुक्त हेडर और प्रतिस्थापन के लिए सुरक्षित <int> को शामिल करना।
रॉबर्ट रमी

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

1

यदि आप उन तरीकों को बहुत अधिक कहेंगे, तो सबसे तेज़ तरीका थोड़ा हेरफेर नहीं होगा, लेकिन शायद एक लुक-अप टेबल होगा। प्रत्येक ऑपरेशन के लिए लंबाई 511 की एक सरणी परिभाषित करें। माइनस (घटाव) के लिए उदाहरण

static unsigned char   maxTable[511];
memset(maxTable, 0, 255);           // If smaller, emulates cutoff at zero
maxTable[255]=0;                    // If equal     - return zero
for (int i=0; i<256; i++)
    maxTable[255+i] = i;            // If greater   - return the difference

सरणी केवल एक बार स्थिर और आरंभिक है। अब आपके घटाव को इनलाइन विधि या पूर्व संकलक के रूप में परिभाषित किया जा सकता है:

#define MINUS(A,B)    maxTable[A-B+255];

यह काम किस प्रकार करता है? वैसे आप अहस्ताक्षरित वर्णों के लिए सभी संभावित घटावों की पूर्व-गणना करना चाहते हैं। परिणाम -५५ से ५:५५ तक अलग-अलग हैं, कुल ५११ अलग-अलग परिणाम हैं। हम सभी संभावित परिणामों की एक सरणी को परिभाषित करते हैं, लेकिन क्योंकि C में हम इसे नकारात्मक सूचकांकों से एक्सेस नहीं कर सकते हैं जिनका हम +255 ([ए-बी + 255] में उपयोग करते हैं)। आप सरणी के केंद्र में एक पॉइंटर को परिभाषित करके इस क्रिया को हटा सकते हैं।

const unsigned char *result = maxTable+255;
#define MINUS(A,B)    result[A-B];

इसका उपयोग करें जैसे:

bsub  = MINUS(13,15); // i.e 13-15 with zero cutoff as requested

ध्यान दें कि निष्पादन बहुत तेज है। परिणाम प्राप्त करने के लिए केवल एक घटाव और एक सूचक deference। कोई शाखा नहीं। स्थिर सरणियाँ बहुत कम हैं, इसलिए गणना की गति को और तेज करने के लिए उन्हें पूरी तरह से सीपीयू के कैश में लोड किया जाएगा

सेम इसके अलावा के लिए काम करेगा, लेकिन एक अलग तालिका के साथ (पहले 256 तत्व सूचक होंगे और अंतिम 255 तत्व 255 से परे कटऑफ का अनुकरण करने के लिए 255 के बराबर होंगे।

यदि आप बिट्स ऑपरेशन पर जोर देते हैं, तो उपयोग किए जाने वाले उत्तर (a> b) गलत हैं। यह अभी भी शाखाओं के रूप में लागू किया जा सकता है। साइन-बिट तकनीक का उपयोग करें

// (num1>num2) ? 1 : 0
#define        is_int_biggerNotEqual( num1,num2) ((((__int32)((num2)-(num1)))&0x80000000)>>31)

अब आप इसका उपयोग घटाव और जोड़ की गणना के लिए कर सकते हैं।

यदि आप कार्यों का अधिकतम उपयोग करना चाहते हैं (), मिनट () बिना शाखा उपयोग के:

inline __int32 MIN_INT(__int32 x, __int32 y){   __int32 d=x-y; return y+(d&(d>>31)); }              

inline __int32 MAX_INT(__int32 x, __int32 y){   __int32 d=x-y; return x-(d&(d>>31)); }

ऊपर मेरे उदाहरण 32 बिट पूर्णांक का उपयोग करते हैं। आप इसे 64 में बदल सकते हैं, हालांकि मेरा मानना ​​है कि 32 बिट की गणना थोड़ी तेज चलती है। आप पर निर्भर करता है


2
यह संभवतः नहीं होगा, वास्तव में: पहला, निश्चित रूप से, टेबल को लोड करना धीमा है। बिट ऑपरेशंस में 1 चक्र लगता है, मेमोरी से लोड होने में लगभग 80 ns लगते हैं; L1 कैश से भी हम 20 ns की सीमा में हैं, जो कि 3GHz CPU पर लगभग 7 चक्र है।
17

आप पूरी तरह से सही नहीं हैं। LUT विधि कुछ चक्र लेगी लेकिन बिट हेरफेर एक चक्र भी नहीं है। कुछ अनुक्रमिक क्रियाएं हैं। उदाहरण के लिए, केवल MAX () की गणना करने के लिए 2 घटाव की आवश्यकता होती है, और तार्किक संचालन और एक पारी सही है। और पूर्णांक पदोन्नति / पदावनति को न भूलें
डैनियलएचएचएच

1
मेरा कहने का मतलब है कि सिंगल बिटवाइज़ ऑपरेशंस में 1 चक्र लगता है, स्वाभाविक रूप से रजिस्टर ऑपरेंड्स। शाफ़िक ने जो कोड दिखाया, उसके साथ क्लैंग 4 प्राथमिक निर्देशों को आउटपुट करता है। इसके अलावा (x > y), शाखाहीन है।
edmz

सबसे पहले, (x> y) ब्रांचिंग का उपयोग कर सकता है। आप नहीं जानते कि आप किस वास्तुकला पर चल रहे हैं। मैं सहमत हूँ कि यह संभवतः इंटेल आर्किटेक्चर पर शाखाविहीन है। अधिकांश स्मार्टफोन इंटेल नहीं हैं। यह भी कारण है कि आप नहीं जान सकते कि कितने विधानसभा निर्देश होंगे। अपने पीसी पर मेरे समाधान की कोशिश करो। मुझे परिणाम सुनने में दिलचस्पी है।
डैनियल एचएचएच

1
L1 कैश 20 ns की तुलना में बहुत तेज है, यह शायद 4 प्रोसेसर चक्रों के क्रम में है। और संभवतः एक अन्यथा अप्रयुक्त निष्पादन इकाई का उपयोग किया जाएगा, और वैसे भी पूरी तरह से पाइपलाइन किया जाएगा। इसे मापो। और 3 गीगाहर्ट्ज के सीपीयू में 20 एस 60 चक्र है।
gnasher729
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.