C में पूर्णांक में उच्चतम सेट बिट (msb) को खोजने का सबसे तेज़ / सबसे कुशल तरीका क्या है?


119

यदि मेरे पास कुछ पूर्णांक n है, और मैं सबसे महत्वपूर्ण बिट की स्थिति जानना चाहता हूं (अर्थात, यदि सबसे कम बिट थोड़ा दाईं ओर है, तो मैं सबसे बाएं बिट की स्थिति जानना चाहता हूं जो कि 1 है), पता लगाने का सबसे तेज / सबसे कुशल तरीका क्या है?

मुझे पता है कि POSIX ffs()स्ट्रिंग्स में एक विधि का समर्थन करता है । पहला सेट बिट खोजने के लिए, लेकिन यह एक संगत fls()विधि नहीं है।

क्या ऐसा करने का कोई स्पष्ट तरीका है जो मुझे याद आ रहा है?

उन मामलों के बारे में जहां आप पोर्टेबिलिटी के लिए POSIX फ़ंक्शन का उपयोग नहीं कर सकते हैं?

संपादित करें: 32 और 64 बिट आर्किटेक्चर पर काम करने वाले एक समाधान के बारे में क्या है (कोड लिस्टिंग में से कई ऐसा लगता है कि वे केवल 32 बिट इनट्स पर काम करेंगे)।


यहाँ कुछ कार्यान्वयन हैं: graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightLinear (संपादित करें: आपके प्रश्न का पुन: निर्धारण करने के बाद, मुझे पता चलता है कि ऊपर दिया गया लिंक सबसे सही सेट बिट खोजने के लिए है, जैसा कि आप की आवश्यकता के बिना नहीं है। शब्द के आकार की समझ, यह जवाब देने के लिए एक मुश्किल है)
खर्च करने वाले


यह सही पर शून्य गिना जाता है ; सवाल बाईं ओर शून्य के बारे में था। कम से कम, एक त्वरित स्किम में मैं इसे वहां नहीं देखता।
डेरियस बेकन

2
क्या आप विशेष रूप से बिट संख्या 'एन' चाहते हैं, या 2 ^ एन पर्याप्त होगा?
अलनीतक

1
"लॉग बेस 2" एल्गोरिदम को देखें - जैसा कि एंडरसन लेख में कहते हैं: "पूर्णांक का लॉग बेस 2 उच्चतम बिट सेट (या सबसे महत्वपूर्ण बिट सेट, एमएसबी) की स्थिति के समान है"
माइकल ब्यूर

जवाबों:


64

GCC में है :

 अंतर्निहित फ़ंक्शन: int __builtin_clz (अहस्ताक्षरित int x)
     एक्स में अग्रणी 0-बिट्स की संख्या लौटाता है, जो सबसे अधिक शुरू होता है
     महत्वपूर्ण बिट स्थिति। यदि X 0 है, तो परिणाम अपरिभाषित है।

 निर्मित में समारोह: int __builtin_clzl (लंबे समय तक अहस्ताक्षरित)
     तर्क प्रकार को छोड़कर `__builtin_clz 'के समान,' अहस्ताक्षरित 'है
     लंबा'।

 निर्मित में समारोह: int __builtin_clzll (लंबे समय तक अहस्ताक्षरित)
     तर्क प्रकार को छोड़कर `__builtin_clz 'के समान,' अहस्ताक्षरित 'है
     लम्बा लम्बा'।

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


एक उपयोगी चाल आपके इनपुट अगर कर सकते हैं हो सकता है शून्य है __builtin_clz(x | 1): बिना शर्त किसी भी अन्य को संशोधित किए बिना कम बिट की स्थापना उत्पादन करता है 31के लिए x=0, किसी भी अन्य इनपुट के लिए उत्पादन को बदले बिना।

ऐसा करने की आवश्यकता से बचने के लिए, आपका अन्य विकल्प मंच-विशिष्ट आंतरिक है जैसे एआरएम जीसीसी __clz(कोई हेडर की आवश्यकता नहीं), या _lzcnt_u32सीपीयू के x86 जो lzcntनिर्देश का समर्थन करते हैं । (खबरदार जो पुराने सीपीयू को फाल्ट करने के बजाए lzcntडिकोड करता है bsr, जो नॉन-जीरो इनपुट के लिए 31-lzcnt देता है।)

दुर्भाग्य से गैर- x86 प्लेटफार्मों पर विभिन्न सीएलजेड निर्देशों का लाभ उठाने का कोई तरीका नहीं है जो इनपुट = 0 के परिणाम को 32 या 64 (ऑपरेंड चौड़ाई के अनुसार) के रूप में परिभाषित करते हैं। x86 का lzcnt, वह भी, जबकि bsrएक बिट-इंडेक्स बनाता है जिसे कंपाइलर को तब तक फ्लिप करना होता है जब तक आप उपयोग नहीं करते हैं 31-__builtin_clz(x)

("अपरिभाषित परिणाम" सी अपरिभाषित व्यवहार नहीं है, बस एक मूल्य है जिसे परिभाषित नहीं किया गया है। यह वास्तव में निर्देश के चलने पर गंतव्य रजिस्टर में जो कुछ भी था। एएमडी दस्तावेज यह कहते हैं, लेकिन इंटेल का सीपीयू उस व्यवहार को लागू नहीं करता है। लेकिन यह वह सब कुछ नहीं है जो आप जिस सी वैरिएबल में पहले कर रहे थे, वह आमतौर पर नहीं है कि जब सी सी सी में एसम बदल जाता है तो चीजें कैसे काम करती हैं। यह भी देखें कि LZCNT मामले की "आउटपुट निर्भरता" को तोड़ना क्यों है? )



1
अपरिभाषित-पर-शून्य व्यवहार उन्हें x86 पर एक एकल बीएसआर निर्देश को संकलित करने देता है, तब भी जब LZCNN उपलब्ध नहीं है। यह __builtin_ctzओवर के लिए एक बड़ा लाभ है ffs, जो बीएसएफ और सीएमओवी को इनपुट-ए-जीरो केस को संभालने के लिए संकलित करता है। कम-पर्याप्त कार्यान्वयन के बिना आर्किटेक्चर पर ( clzनिर्देश के बिना पुराने एआरएम ), gcc एक libgcc सहायक फ़ंक्शन को कॉल का उत्सर्जन करता है।
पीटर कॉर्ड्स

41

मान लें कि आप x86 पर हैं और बिट इनलाइन असेंबलर के लिए गेम, इंटेल एक BSRनिर्देश प्रदान करता है ("बिट स्कैन रिवर्स")। यह तेजी से पर कुछ x86s (दूसरों पर microcoded)। मैनुअल से:

सबसे महत्वपूर्ण सेट बिट (1 बिट) के लिए स्रोत ऑपरेंड की खोज करता है। यदि सबसे महत्वपूर्ण 1 बिट पाया जाता है, तो इसका बिट इंडेक्स गंतव्य ऑपरेंड में संग्रहीत किया जाता है। स्रोत ऑपरेंड एक रजिस्टर या एक स्मृति स्थान हो सकता है; गंतव्य ऑपरेंड एक रजिस्टर है। बिट इंडेक्स स्रोत ऑपरेंड के बिट 0 से एक अहस्ताक्षरित ऑफसेट है। यदि सामग्री स्रोत ऑपरेंड 0 है, तो गंतव्य ऑपरेंड की सामग्री अपरिभाषित है।

(यदि आप PowerPC पर हैं तो एक समान cntlz("अग्रणी शून्य गणना करें") निर्देश है।

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

#include <iostream>

int main (int,char**)
{
  int n=1;
  for (;;++n) {
    int msb;
    asm("bsrl %1,%0" : "=r"(msb) : "r"(n));
    std::cout << n << " : " << msb << std::endl;
  }
  return 0;
}

इस इनलाइन कोडांतरक ट्यूटोरियल को भी देखें , जो दिखाता है (धारा 9.4) यह लूपिंग कोड की तुलना में काफी तेज है।


4
वास्तव में यह निर्देश आमतौर पर लूप में माइक्रोकोडेड होता है और बल्कि धीमा होता है।
rlbond

2
कौनसा ? BSR या CNTLZ? जैसा कि मैंने x86- टाइमिंग .pdf ऊपर संदर्भित पढ़ा है, बीएसआर केवल नेटबर्स्ट पेंटियम पर धीमा है। हालांकि मुझे पावरपीसी के बारे में कुछ नहीं पता है।
तिमय

5
... ठीक है, घनिष्ठ निरीक्षण पर कि "बीएसआर केवल पी 3 / पेंटियम-एम / कोर 2 x86s पर तेज है"। नेटबर्स्ट और एएमडी पर धीमा।
तिमय

1
बस एक सिर: आपके पिछले दो लिंक मर चुके हैं।
बौम Augen mit

2
@ सर्बोंड: हुह, पी 4 प्रेस्कॉट पर बीएसआर 16 चक्र विलंबता (!) के साथ 2 उफ़ है, प्रति 4c थ्रूपुट के साथ। लेकिन पहले के नेटबर्स्ट पर, यह केवल 4 चक्र विलंबता (अभी भी 2 उफ़) है, और प्रति 2c थ्रूपुट है। (source: agner.org/optimize ) अधिकांश सीपीयू पर, इसके आउटपुट पर निर्भरता भी होती है, जिसके लिए जीसीसी खाता नहीं है (जब इनपुट शून्य है, तो वास्तविक व्यवहार अपरिवर्तित गंतव्य को छोड़ना है)। इससे stackoverflow.com/questions/25078285/… जैसी समस्याएं हो सकती हैं । आईडीके ने फिक्सिंग के दौरान बीएसआर को क्यों मिस किया।
पीटर कॉर्ड्स

38

चूँकि 2 ^ N केवल Nth बिट सेट (1 << N) के साथ एक पूर्णांक है, इसलिए उच्चतम सेट बिट की स्थिति (N) पूर्णांक लॉग आधार 2 पूर्णांक है।

http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious

unsigned int v;
unsigned r = 0;

while (v >>= 1) {
    r++;
}

यह "स्पष्ट" एल्गोरिथ्म सभी के लिए पारदर्शी नहीं हो सकता है, लेकिन जब आपको पता चलता है कि कोड एक बार दाईं ओर बार-बार शिफ्ट होता है, जब तक कि बाएं बिट को शिफ्ट नहीं किया जाता है (ध्यान दें कि सी किसी भी गैर-शून्य मान को सही मानता है) और नंबर लौटाता है पारियों की, यह सही समझ में आता है। इसका मतलब यह भी है कि यह तब भी काम करता है जब एक से अधिक बिट सेट होता है - परिणाम हमेशा सबसे महत्वपूर्ण बिट के लिए होता है।

यदि आप उस पृष्ठ पर नीचे स्क्रॉल करते हैं, तो तेज़, अधिक जटिल विविधताएँ हैं। हालांकि, यदि आप जानते हैं कि आप बहुत सारे अग्रणी शून्य के साथ संख्याओं के साथ काम कर रहे हैं, तो भोली दृष्टिकोण स्वीकार्य गति प्रदान कर सकता है, क्योंकि बिट शिफ्टिंग सी में तेजी से होती है, और सरल एल्गोरिथ्म को किसी सरणी को अनुक्रमित करने की आवश्यकता नहीं होती है।

नोट: 64-बिट मान का उपयोग करते समय, अतिरिक्त-चतुर एल्गोरिदम का उपयोग करने के बारे में बेहद सतर्क रहें; उनमें से कई केवल 32-बिट मानों के लिए सही ढंग से काम करते हैं।


2
@Johan एक डीबगर के माध्यम से कदम से यह समझाने में मदद कर सकता है कि लूप क्यों निकलता है। मूल रूप से, इसकी 'क्योंकि स्थिति में अभिव्यक्ति 0 का मूल्यांकन करती है (जिसे झूठा माना जाता है) एक बार अंतिम 1 बिट को दाईं ओर स्थानांतरित कर दिया गया था।
क्विन टेलर

2
अंतिम परिणाम का उपयोग करने का अच्छा विचार :) :)
जोहान

6
नोट: हस्ताक्षरित पूर्णांक के लिए, अहस्ताक्षरित होना चाहिए, नकारात्मक संख्याओं के लिए सही बदलाव विफल रहता है।
Xantix

2
Xantix: C / C ++ में बदलाव एक तार्किक बदलाव है, इसलिए यह ठीक काम करता है। जावा, जावास्क्रिप्ट या डी के लिए, आपको तार्किक शिफ्ट ऑपरेटर का उपयोग करने की आवश्यकता है >>>। इसके अलावा शायद तुलनित्र != 0, और कोष्ठक की कुछ अनिर्दिष्ट संख्या।
चेस

8
@ चर्चा: नहीं, यह नहीं है। यह अहस्ताक्षरित के लिए एक तार्किक बदलाव है । के लिए हस्ताक्षर किए हैं, यह या नहीं हो सकता है एक तार्किक बदलाव हो (और यह आमतौर पर गणित है, वास्तव में)।
टिम Timस

17

यह तेज़ बिजली होनी चाहिए:

int msb(unsigned int v) {
  static const int pos[32] = {0, 1, 28, 2, 29, 14, 24, 3,
    30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19,
    16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
  v |= v >> 1;
  v |= v >> 2;
  v |= v >> 4;
  v |= v >> 8;
  v |= v >> 16;
  v = (v >> 1) + 1;
  return pos[(v * 0x077CB531UL) >> 27];
}

25
7 बिट शिफ्ट, 5 या निर्देश, एक गुणक और एक संभावित कैश मिस। :) क्या आपने इसे बेंचमार्क किया, या उत्पन्न कोडर को देखो? यह काफी धीमा खत्म हो सकता है , यह इस बात पर निर्भर करता है कि कंपाइलर कितना खत्म कर सकता है।
जल्फ

5
मैं यहाँ नया हूँ। मुझे नकारात्मक वोट नहीं मिलते। मैंने स्रोत कोड के साथ एकमात्र उत्तर प्रदान किया है जो वास्तव में काम करता है।
नायक

9
"संभव कैश मिस" संभवतः इस कोड के कारण इसकी लुकअप टेबल तक पहुंच की आवश्यकता है। यदि यह कहे जाने पर उस तालिका को कैश नहीं किया जाता है, तो इसे लाने के दौरान एक स्टाल होगा। यह LUT का उपयोग नहीं करने वाले समाधानों की तुलना में सबसे खराब स्थिति वाला प्रदर्शन अधिक खराब कर सकता है।
खोलना

13
वास्तव में बात नहीं है। यह आवश्यकता से अधिक डेटा कैश का उपयोग करता है (एक से अधिक कैश लाइन, यहां तक ​​कि), और आवश्यकता से अधिक अनुदेश कैश। आपको संभवत: कैश मिसेज़ मिलेंगे जिन्हें आप फ़ंक्शन को कॉल करने से पहली बार में बचा सकते थे, और यह कैश को ज़रूरत से ज़्यादा प्रदूषित कर देगा, इसलिए कॉल के बाद , अन्य कोड आवश्यकता से अधिक मिसाइलों का सामना कर सकते हैं। LUT अक्सर परेशानी के लायक नहीं होता क्योंकि कैश मिस महंगा होता है। लेकिन मैंने केवल यह कहा कि यह कुछ ऐसा था जिसे मैं "लाइटनिंग फास्ट" होने का दावा करने से पहले बेंचमार्क करना चाहता था। ऐसा नहीं है कि यह निश्चित रूप से एक समस्या है।
जुलफ j

6
तालिका में 32 प्रविष्टियाँ हैं, और प्रत्येक मान <255 (127) है, इसलिए तालिका को टाइप अहस्ताक्षरित चार के रूप में परिभाषित करें, और यह एक 32 बाइट एल 1 कैश लाइन में फिट होगा। और पूरी बात दो कैश लाइनों में फिट होती है।
चकचोट्रिल

16

यह एक प्रकार का पूर्णांक लॉग खोजने की तरह है। बिट-टिडलिंग ट्रिक्स हैं, लेकिन मैंने इसके लिए अपना टूल बनाया है। पाठ्यक्रम का लक्ष्य गति के लिए है।

मेरा एहसास है कि सीपीयू में एक स्वचालित बिट-डिटेक्टर पहले से ही है, पूर्णांक के लिए इसका उपयोग फ्लोट रूपांतरण में किया जाता है! तो उस का उपयोग करें।

double ff=(double)(v|1);
return ((*(1+(uint32_t *)&ff))>>20)-1023;  // assumes x86 endianness

यह संस्करण मूल्य को दोगुना कर देता है, फिर घातांक को पढ़ता है, जो बताता है कि बिट कहां था। फैंसी शिफ्ट और घटाना IEEE मान से उचित भागों को निकालने के लिए है।

यह फ़्लोट्स का उपयोग करने के लिए थोड़ा तेज़ है, लेकिन एक फ़्लोट आपको इसकी छोटी परिशुद्धता के कारण केवल पहले 24 बिट स्थान दे सकता है।


C ++ या C में अपरिभाषित व्यवहार के बिना, इसे सुरक्षित रूप से करने के लिए, memcpyटाइप-पिंगिंग के लिए पॉइंटर कास्टिंग के बजाय उपयोग करें । कंपाइलर जानते हैं कि यह कैसे कुशलतापूर्वक इनलाइन करता है।

// static_assert(sizeof(double) == 2 * sizeof(uint32_t), "double isn't 8-byte IEEE binary64");
// and also static_assert something about FLT_ENDIAN?

double ff=(double)(v|1);

uint32_t tmp;
memcpy(&tmp, ((const char*)&ff)+sizeof(uint32_t), sizeof(uint32_t));
return (tmp>>20)-1023;

या C99 और बाद में, a का उपयोग करें union {double d; uint32_t u[2];};। लेकिन ध्यान दें कि C ++ में, संघ प्रकार की पैंटिंग केवल कुछ संकलक पर एक एक्सटेंशन के रूप में समर्थित है, आईएसओ C ++ में नहीं।


यह आमतौर पर एक अग्रणी-शून्य गणना निर्देश के लिए एक मंच-विशिष्ट आंतरिक से धीमी होगी, लेकिन पोर्टेबल आईएसओ सी में ऐसा कोई फ़ंक्शन नहीं है। कुछ सीपीयू में एक अग्रणी-शून्य गिनती निर्देश की कमी होती है, लेकिन उनमें से कुछ कुशलतापूर्वक पूर्णांकों को बदल सकते हैं double। पूर्णांक पर वापस एफपी बिट पैटर्न टाइप करना, धीमा हो सकता है, हालांकि (जैसे पावरपीसी पर इसे स्टोर / रीलोड की आवश्यकता होती है और आमतौर पर लोड-हिट-स्टोर स्टाल का कारण बनता है)।

यह एल्गोरिथ्म संभवतः SIMD कार्यान्वयन के लिए उपयोगी हो सकता है, क्योंकि कम CPU में SIMD होता है lzcnt। x86 को केवल AVX512CD के साथ ऐसा निर्देश मिला


2
हाँ। और जीसी कोड के साथ बुरा काम करेगा -O2 के साथ टाइप-अलियासिंग अनुकूलन के कारण।
एमएसएन

4
पूर्णांक और फ्लोटिंग पॉइंट के बीच कास्टिंग x86 सीपीयू
जालफ

1
हां, एफपीयू की लागत अधिक है। लेकिन वास्तविक समय मापों से पता चला कि यह ऑल-बिट ऑप्स या विशेष रूप से किसी भी छोरों की तुलना में तेज था। यह कोशिश करो और सबसे तेजी से ले लो हमेशा सबसे अच्छी सलाह है। हालाँकि मुझे इसके साथ GCC और -O2 की कोई समस्या नहीं है।
SPWorley

1
क्या यह अपरिभाषित व्यवहार नहीं है (एक असंगत प्रकार के सूचक के माध्यम से एक मूल्य पढ़ना)?
ड्रीमलैक्स

3
हैकर की डिलाईट बताती है कि 5-3 काउंटिंग लीडिंग 0 में 32-बिट फ्लोट में त्रुटि के लिए कैसे सही किया जाए। यहाँ उनका कोड है, जो asloloat और asInt को ओवरलैप करने के लिए एक अनाम संघ का उपयोग करता है: k = k & ~ (k >> 1); asFloat = (float) k + 0.5f; n = 158 - (asInt >> 23); (और हाँ, यह कार्यान्वयन-परिभाषित व्यवहार पर निर्भर करता है)
डी कोएट्ज़ी

11

यहाँ काज़ कल्हेकु

मैंने इसके लिए 63 बिट संख्या (gcc x86_64 पर लंबा लंबा प्रकार) के लिए दो दृष्टिकोण दिए, जो साइन बिट से दूर रहे।

(मुझे ऐसा लगता है कि किसी चीज़ के लिए आपको "उच्चतम बिट" की आवश्यकता है, आप देखें।)

मैंने डेटा-संचालित बाइनरी खोज (उपरोक्त उत्तरों में से एक पर बारीकी से आधारित) को लागू किया। मैंने हाथ से पूरी तरह से अनियंत्रित निर्णय वृक्ष भी लागू किया, जो तत्काल ऑपरेंड के साथ कोड है। कोई लूप नहीं, कोई टेबल नहीं।

N = 0 मामले को छोड़कर जिस निर्णय के लिए बाइनरी खोज का एक स्पष्ट परीक्षण है, वह निर्णय ट्री (उच्चतम_अंकृत / अनियंत्रित) 69% अधिक तेज़ होना है।

बाइनरी-सर्च का विशेष परीक्षण 0 केस के लिए केवल निर्णय वृक्ष की तुलना में 48% अधिक तेज है, जिसमें एक विशेष परीक्षण नहीं है।

कम्पाइलर, मशीन: (GCC 4.5.2, -O3, x86-64, 2867 मेगाहर्ट्ज इंटेल कोर i5)।

int highest_bit_unrolled(long long n)
{
  if (n & 0x7FFFFFFF00000000) {
    if (n & 0x7FFF000000000000) {
      if (n & 0x7F00000000000000) {
        if (n & 0x7000000000000000) {
          if (n & 0x4000000000000000)
            return 63;
          else
            return (n & 0x2000000000000000) ? 62 : 61;
        } else {
          if (n & 0x0C00000000000000)
            return (n & 0x0800000000000000) ? 60 : 59;
          else
            return (n & 0x0200000000000000) ? 58 : 57;
        }
      } else {
        if (n & 0x00F0000000000000) {
          if (n & 0x00C0000000000000)
            return (n & 0x0080000000000000) ? 56 : 55;
          else
            return (n & 0x0020000000000000) ? 54 : 53;
        } else {
          if (n & 0x000C000000000000)
            return (n & 0x0008000000000000) ? 52 : 51;
          else
            return (n & 0x0002000000000000) ? 50 : 49;
        }
      }
    } else {
      if (n & 0x0000FF0000000000) {
        if (n & 0x0000F00000000000) {
          if (n & 0x0000C00000000000)
            return (n & 0x0000800000000000) ? 48 : 47;
          else
            return (n & 0x0000200000000000) ? 46 : 45;
        } else {
          if (n & 0x00000C0000000000)
            return (n & 0x0000080000000000) ? 44 : 43;
          else
            return (n & 0x0000020000000000) ? 42 : 41;
        }
      } else {
        if (n & 0x000000F000000000) {
          if (n & 0x000000C000000000)
            return (n & 0x0000008000000000) ? 40 : 39;
          else
            return (n & 0x0000002000000000) ? 38 : 37;
        } else {
          if (n & 0x0000000C00000000)
            return (n & 0x0000000800000000) ? 36 : 35;
          else
            return (n & 0x0000000200000000) ? 34 : 33;
        }
      }
    }
  } else {
    if (n & 0x00000000FFFF0000) {
      if (n & 0x00000000FF000000) {
        if (n & 0x00000000F0000000) {
          if (n & 0x00000000C0000000)
            return (n & 0x0000000080000000) ? 32 : 31;
          else
            return (n & 0x0000000020000000) ? 30 : 29;
        } else {
          if (n & 0x000000000C000000)
            return (n & 0x0000000008000000) ? 28 : 27;
          else
            return (n & 0x0000000002000000) ? 26 : 25;
        }
      } else {
        if (n & 0x0000000000F00000) {
          if (n & 0x0000000000C00000)
            return (n & 0x0000000000800000) ? 24 : 23;
          else
            return (n & 0x0000000000200000) ? 22 : 21;
        } else {
          if (n & 0x00000000000C0000)
            return (n & 0x0000000000080000) ? 20 : 19;
          else
            return (n & 0x0000000000020000) ? 18 : 17;
        }
      }
    } else {
      if (n & 0x000000000000FF00) {
        if (n & 0x000000000000F000) {
          if (n & 0x000000000000C000)
            return (n & 0x0000000000008000) ? 16 : 15;
          else
            return (n & 0x0000000000002000) ? 14 : 13;
        } else {
          if (n & 0x0000000000000C00)
            return (n & 0x0000000000000800) ? 12 : 11;
          else
            return (n & 0x0000000000000200) ? 10 : 9;
        }
      } else {
        if (n & 0x00000000000000F0) {
          if (n & 0x00000000000000C0)
            return (n & 0x0000000000000080) ? 8 : 7;
          else
            return (n & 0x0000000000000020) ? 6 : 5;
        } else {
          if (n & 0x000000000000000C)
            return (n & 0x0000000000000008) ? 4 : 3;
          else
            return (n & 0x0000000000000002) ? 2 : (n ? 1 : 0);
        }
      }
    }
  }
}

int highest_bit(long long n)
{
  const long long mask[] = {
    0x000000007FFFFFFF,
    0x000000000000FFFF,
    0x00000000000000FF,
    0x000000000000000F,
    0x0000000000000003,
    0x0000000000000001
  };
  int hi = 64;
  int lo = 0;
  int i = 0;

  if (n == 0)
    return 0;

  for (i = 0; i < sizeof mask / sizeof mask[0]; i++) {
    int mi = lo + (hi - lo) / 2;

    if ((n >> mi) != 0)
      lo = mi;
    else if ((n & (mask[i] << lo)) != 0)
      hi = mi;
  }

  return lo + 1;
}

त्वरित और गंदा परीक्षण कार्यक्रम:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int highest_bit_unrolled(long long n);
int highest_bit(long long n);

main(int argc, char **argv)
{
  long long n = strtoull(argv[1], NULL, 0);
  int b1, b2;
  long i;
  clock_t start = clock(), mid, end;

  for (i = 0; i < 1000000000; i++)
    b1 = highest_bit_unrolled(n);

  mid = clock();

  for (i = 0; i < 1000000000; i++)
    b2 = highest_bit(n);

  end = clock();

  printf("highest bit of 0x%llx/%lld = %d, %d\n", n, n, b1, b2);

  printf("time1 = %d\n", (int) (mid - start));
  printf("time2 = %d\n", (int) (end - mid));
  return 0;
}

केवल -O2 के उपयोग से, अंतर अधिक हो जाता है। निर्णय का पेड़ लगभग चार गुना तेज है।

मैंने भोली बिट शिफ्टिंग कोड के खिलाफ भी बेंचमार्क किया:

int highest_bit_shift(long long n)
{
  int i = 0;
  for (; n; n >>= 1, i++)
    ; /* empty */
  return i;
}

यह केवल छोटी संख्याओं के लिए तेज़ है, क्योंकि कोई भी उम्मीद करेगा। यह निर्धारित करने में कि उच्चतम बिट n == 1 के लिए 1 है, इसने 80% से अधिक तेजी से बेंचमार्क किया। हालाँकि, 63 बिट स्पेस में आधे बेतरतीब ढंग से चुने गए नंबरों में 63 वां बिट सेट है!

इनपुट 0x3FFFFFFFFFFFFFFFFF पर, निर्णय ट्री संस्करण 1 की तुलना में काफी तेज है, और बिट शिफ्टर की तुलना में 1120% तेज (12.2 गुना) होना दिखाता है।

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


9
मैं यह नहीं कह रहा हूं कि यह अच्छा नहीं है, लेकिन यहां आपका परीक्षण कार्यक्रम केवल उसी संख्या पर परीक्षण करता है, जिसके बाद 2-3 पुनरावृत्तियों ने शाखा के भविष्यवक्ताओं को उनकी अंतिम स्थिति पर सेट कर दिया होगा और उसके बाद वे सही शाखा भविष्यवाणियां करेंगे। अच्छी बात यह है कि पूरी तरह से रैंडम वितरण के साथ आधे नंबरों के पास सही भविष्यवाणी के करीब होगा, अर्थात् बिट 63।
सर्ट


6
unsigned int
msb32(register unsigned int x)
{
        x |= (x >> 1);
        x |= (x >> 2);
        x |= (x >> 4);
        x |= (x >> 8);
        x |= (x >> 16);
        return(x & ~(x >> 1));
}

1 रजिस्टर, 13 निर्देश। मानो या न मानो, यह आमतौर पर ऊपर उल्लिखित बीएसआर निर्देश से तेज है, जो रैखिक समय में संचालित होता है। यह लॉगरिदमिक समय है।

से http://aggregate.org/MAGIC/#Most%20Significant%201%20Bit


7
उपरोक्त कोड प्रश्न का उत्तर नहीं देता है। यह एक अहस्ताक्षरित पूर्णांक देता है जहां x में बिट पर सबसे महत्वपूर्ण रहता है और अन्य सभी बिट्स बंद हो जाते हैं। सवाल यह था कि बिट पर सबसे महत्वपूर्ण की स्थिति को वापस करना है ।
नायक

3
फिर आप उस बिट के सूचकांक को खोजने के लिए एक डी ब्रूजन अनुक्रम दृष्टिकोण का उपयोग कर सकते हैं। :-)
आर .. गिटहब स्टॉप मदद

5
@ विरोधी, उन्होंने कहा कि या तो पर्याप्त है।
rlbond

यह एक (उसी पृष्ठ से) वह करेगा जो आपको चाहिए, लेकिन इसके लिए एक अतिरिक्त फ़ंक्शन की आवश्यकता होती है। समुच्चय ./MAGIC/#Log2%20of%20an%20Integer
क्विन टेलर

1
कम से कम कोर 2 के बाद से इंटेल सीपीयू पर बीएसआर तेज है। एएमडी सीपीयू पर LZCNT तेज है, और __builtin_clzअगर यह इसके साथ -march=nativeया कुछ के लिए सक्षम है तो gcc इसका उपयोग करता है (क्योंकि यह हर सीपीयू पर तेज है जो इसका समर्थन करता है)। एएमडी बुलडोजर-परिवार जैसे सीपीयू पर भी जहां बीएसआर "धीमा" है, यह इतना धीमा नहीं है: 4 चक्र विलंबता और 4 सी थ्रूपुट के साथ 7 मीटर ऑप्स। एटम पर, बीएसआर वास्तव में धीमा है: 16 चक्र। सिल्वरमोंट पर, यह 10 चक्र विलंबता के साथ 10 उफ़ है। यह सिल्वरमोंट पर बीएसआर की तुलना में थोड़ा कम विलंब हो सकता है, लेकिन आईडीके।
पीटर कॉर्डेस

6

इस पृष्ठ पर वर्तमान में दिए गए एल्गोरिदम के कुछ (सरल) बेंचमार्क हैं ...

अहस्ताक्षरित int के सभी इनपुट पर एल्गोरिदम का परीक्षण नहीं किया गया है; इसलिए जाँच करें कि पहले, आँख बंद करके कुछ का उपयोग करने से पहले;)

मेरी मशीन पर clz (__builtin_clz) और asm सबसे अच्छा काम करते हैं। asm भी तेज लगता है तो clz ... लेकिन यह साधारण बेंचमार्क के कारण हो सकता है ...

//////// go.c ///////////////////////////////
// compile with:  gcc go.c -o go -lm
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/***************** math ********************/

#define POS_OF_HIGHESTBITmath(a) /* 0th position is the Least-Signif-Bit */    \
  ((unsigned) log2(a))         /* thus: do not use if a <= 0 */  

#define NUM_OF_HIGHESTBITmath(a) ((a)               \
                  ? (1U << POS_OF_HIGHESTBITmath(a))    \
                  : 0)



/***************** clz ********************/

unsigned NUM_BITS_U = ((sizeof(unsigned) << 3) - 1);
#define POS_OF_HIGHESTBITclz(a) (NUM_BITS_U - __builtin_clz(a)) /* only works for a != 0 */

#define NUM_OF_HIGHESTBITclz(a) ((a)                    \
                 ? (1U << POS_OF_HIGHESTBITclz(a))  \
                 : 0)


/***************** i2f ********************/

double FF;
#define POS_OF_HIGHESTBITi2f(a) (FF = (double)(ui|1), ((*(1+(unsigned*)&FF))>>20)-1023)


#define NUM_OF_HIGHESTBITi2f(a) ((a)                    \
                 ? (1U << POS_OF_HIGHESTBITi2f(a))  \
                 : 0)




/***************** asm ********************/

unsigned OUT;
#define POS_OF_HIGHESTBITasm(a) (({asm("bsrl %1,%0" : "=r"(OUT) : "r"(a));}), OUT)

#define NUM_OF_HIGHESTBITasm(a) ((a)                    \
                 ? (1U << POS_OF_HIGHESTBITasm(a))  \
                 : 0)




/***************** bitshift1 ********************/

#define NUM_OF_HIGHESTBITbitshift1(a) (({   \
  OUT = a;                  \
  OUT |= (OUT >> 1);                \
  OUT |= (OUT >> 2);                \
  OUT |= (OUT >> 4);                \
  OUT |= (OUT >> 8);                \
  OUT |= (OUT >> 16);               \
      }), (OUT & ~(OUT >> 1)))          \



/***************** bitshift2 ********************/
int POS[32] = {0, 1, 28, 2, 29, 14, 24, 3,
             30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19,
             16, 7, 26, 12, 18, 6, 11, 5, 10, 9};

#define POS_OF_HIGHESTBITbitshift2(a) (({   \
  OUT = a;                  \
  OUT |= OUT >> 1;              \
  OUT |= OUT >> 2;              \
  OUT |= OUT >> 4;              \
  OUT |= OUT >> 8;              \
  OUT |= OUT >> 16;             \
  OUT = (OUT >> 1) + 1;             \
      }), POS[(OUT * 0x077CB531UL) >> 27])

#define NUM_OF_HIGHESTBITbitshift2(a) ((a)              \
                       ? (1U << POS_OF_HIGHESTBITbitshift2(a)) \
                       : 0)



#define LOOPS 100000000U

int main()
{
  time_t start, end;
  unsigned ui;
  unsigned n;

  /********* Checking the first few unsigned values (you'll need to check all if you want to use an algorithm here) **************/
  printf("math\n");
  for (ui = 0U; ui < 18; ++ui)
    printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITmath(ui));

  printf("\n\n");

  printf("clz\n");
  for (ui = 0U; ui < 18U; ++ui)
    printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITclz(ui));

  printf("\n\n");

  printf("i2f\n");
  for (ui = 0U; ui < 18U; ++ui)
    printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITi2f(ui));

  printf("\n\n");

  printf("asm\n");
  for (ui = 0U; ui < 18U; ++ui) {
    printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITasm(ui));
  }

  printf("\n\n");

  printf("bitshift1\n");
  for (ui = 0U; ui < 18U; ++ui) {
    printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITbitshift1(ui));
  }

  printf("\n\n");

  printf("bitshift2\n");
  for (ui = 0U; ui < 18U; ++ui) {
    printf("%i\t%i\n", ui, NUM_OF_HIGHESTBITbitshift2(ui));
  }

  printf("\n\nPlease wait...\n\n");


  /************************* Simple clock() benchmark ******************/
  start = clock();
  for (ui = 0; ui < LOOPS; ++ui)
    n = NUM_OF_HIGHESTBITmath(ui);
  end = clock();
  printf("math:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);

  start = clock();
  for (ui = 0; ui < LOOPS; ++ui)
    n = NUM_OF_HIGHESTBITclz(ui);
  end = clock();
  printf("clz:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);

  start = clock();
  for (ui = 0; ui < LOOPS; ++ui)
    n = NUM_OF_HIGHESTBITi2f(ui);
  end = clock();
  printf("i2f:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);

  start = clock();
  for (ui = 0; ui < LOOPS; ++ui)
    n = NUM_OF_HIGHESTBITasm(ui);
  end = clock();
  printf("asm:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);

  start = clock();
  for (ui = 0; ui < LOOPS; ++ui)
    n = NUM_OF_HIGHESTBITbitshift1(ui);
  end = clock();
  printf("bitshift1:\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);

  start = clock();
  for (ui = 0; ui < LOOPS; ++ui)
    n = NUM_OF_HIGHESTBITbitshift2(ui);
  end = clock();
  printf("bitshift2\t%e\n", (double)(end-start)/CLOCKS_PER_SEC);

  printf("\nThe lower, the better. Take note that a negative exponent is good! ;)\n");

  return EXIT_SUCCESS;
}

6

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

[...], bsrlविधानसभा निर्देश सबसे महत्वपूर्ण बिट की स्थिति की गणना करता है। इस प्रकार, हम इस asmकथन का उपयोग कर सकते हैं :

asm ("bsrl %1, %0" 
     : "=r" (position) 
     : "r" (number));

विस्तार करने के लिए: मानक लूप समाधान (बाएं शिफ्टिंग और एमएसबी की जांच) शायद सबसे पठनीय है। जैसे कि सभी मामलों में बिट ट्विडलिंग शामिल है, एएसएम की गति को पीटा नहीं जा सकता है, हालांकि आवश्यक होने तक आपके कोड को अव्यवस्थित करने का कोई मतलब नहीं है। भाड़े एक के बीच समाधान हैं - एक तरह से या दूसरे पर जाएं।
नोल्डोरिन

मैं कहता हूं कि लॉगरिदम लेना एक पूरी तरह से पठनीय समाधान होगा (उत्पन्न एसम को यह देखने के लिए देखें कि क्या कंपाइलर इस एसएमएम निर्देश का उपयोग करने के लिए इसे अनुकूलित कर सकता है)
जल्फ

कभी-कभी सीपीयू माइक्रोकोड में कार्यान्वयन के आधार पर इनलाइन एएसएम समाधान धीमा होता है।
rlbond

5
@ शीघ्र: मैं शायद ही विश्वास कर सकता हूँ, हालाँकि मुझसे गलती हो सकती है। किसी भी आधुनिक सीपीयू पर किसी को लगता है कि यह एक निर्देश के लिए अनुवादित हो जाएगा ....
Noldorin

3
@ नोल्डोरिन यह थोड़ा देर से है लेकिन .. यह एक एकल निर्देश की परिभाषा के अनुसार है, लेकिन अगर यह आरबॉन्ड के रूप में माइक्रोकोड है तो यह एकल निर्देश आंतरिक रूप से µops के पूरे समूह को डिकोड कर सकता है। यह AMD के माइक्रोआर्किटेक्चर्स, और इंटेल एटम पर मामला होता है, लेकिन सामान्य इंटेल माइक्रोआर्किटेक्चर्स पर यह सभी तरह से एक एकल ऑपरेशन है।
हेरोल्ड

4

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

int highest_bit(unsigned int a) {
  static const unsigned int maskv[] = { 0xffff, 0xff, 0xf, 0x3, 0x1 };
  const unsigned int *mask = maskv;
  int l, h;

  if (a == 0) return -1;

  l = 0;
  h = 32;

  do {
    int m = l + (h - l) / 2;

    if ((a >> m) != 0) l = m;
    else if ((a & (*mask << l)) != 0) h = m;

    mask++;
  } while (l < h - 1);

  return l;
}

4

बाइनरी खोज के कुछ प्रकारों को thats, यह सभी प्रकार के (अहस्ताक्षरित) पूर्णांक प्रकारों के साथ काम करता है

#include <climits>
#define UINT (unsigned int)
#define UINT_BIT (CHAR_BIT*sizeof(UINT))

int msb(UINT x)
{
    if(0 == x)
        return -1;

    int c = 0;

    for(UINT i=UINT_BIT>>1; 0<i; i>>=1)
    if(static_cast<UINT>(x >> i))
    {
        x >>= i;
        c |= i;
    }

    return c;
}

पूरा करने के लिए:

#include <climits>
#define UINT unsigned int
#define UINT_BIT (CHAR_BIT*sizeof(UINT))

int lsb(UINT x)
{
    if(0 == x)
        return -1;

    int c = UINT_BIT-1;

    for(UINT i=UINT_BIT>>1; 0<i; i>>=1)
    if(static_cast<UINT>(x << i))
    {
        x <<= i;
        c ^= i;
    }

    return c;
}

4
कृपया typedefpreprocessor macros को छोड़कर s या वास्तव में कुछ भी के लिए ALL_CAPS का उपयोग नहीं करने पर विचार करें । यह एक व्यापक रूप से स्वीकृत सम्मेलन है।
अंडरस्कोर_ड

4

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

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

u8  inline log2 (u32 val)  {
    u8  k = 0;
    if (val > 0x0000FFFFu) { val >>= 16; k  = 16; }
    if (val > 0x000000FFu) { val >>= 8;  k |= 8;  }
    if (val > 0x0000000Fu) { val >>= 4;  k |= 4;  }
    if (val > 0x00000003u) { val >>= 2;  k |= 2;  }
    k |= (val & 2) >> 1;
    return k;
}

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

हालांकि टेबल्स बेहतर हो सकते हैं, अगर आप इसे बार-बार पर्याप्त बार कॉल करते हैं, तो कैश मिस के जोखिम को एक टेबल के स्पीडअप द्वारा ग्रहण किया जाता है।

u8 kTableLog2[256] = {
0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
};

u8 log2_table(u32 val)  {
    u8  k = 0;
    if (val > 0x0000FFFFuL) { val >>= 16; k  = 16; }
    if (val > 0x000000FFuL) { val >>=  8; k |=  8; }
    k |= kTableLog2[val]; // precompute the Log2 of the low byte

    return k;
}

यह यहां दिए गए किसी भी सॉफ़्टवेयर उत्तर के उच्चतम थ्रूपुट का उत्पादन करना चाहिए, लेकिन यदि आप इसे कभी-कभी कहते हैं, तो मेरी पहली स्निपेट की तरह तालिका-मुक्त समाधान पसंद करें।


1
कुछ उत्तर शाखाविहीन हैं, लेकिन यह शायद सशर्त शाखाओं के साथ संकलित होगा। क्या आपने केवल एक ही मूल्य के साथ बार-बार बेंचमार्क किया, या एक साधारण पैटर्न या कुछ और? प्रदर्शन के लिए शाखा गलतफहमी एक हत्यारा है। stackoverflow.com/questions/11227809/…
पीटर कॉर्डेस

3

जैसा कि ऊपर दिए गए उत्तर बताते हैं, सबसे महत्वपूर्ण बिट को निर्धारित करने के कई तरीके हैं। हालाँकि, जैसा कि यह भी बताया गया था, विधियाँ 32bit या 64bit रजिस्टरों के लिए अद्वितीय होने की संभावना है। Stanford.edu bithacks पेज कि दोनों 32bit और 64bit के लिए काम करते कंप्यूटिंग समाधान प्रदान करता है। थोड़ा काम के साथ, उन्हें MSB प्राप्त करने के लिए एक ठोस क्रॉस-आर्किटेक्चर दृष्टिकोण प्रदान करने के लिए जोड़ा जा सकता है। 64/32 बिट कंप्यूटरों पर संकलित / काम करने वाले समाधान पर मैं आया था:

#if defined(__LP64__) || defined(_LP64)
# define BUILD_64   1
#endif

#include <stdio.h>
#include <stdint.h>  /* for uint32_t */

/* CHAR_BIT  (or include limits.h) */
#ifndef CHAR_BIT
#define CHAR_BIT  8
#endif  /* CHAR_BIT */

/* 
 * Find the log base 2 of an integer with the MSB N set in O(N)
 * operations. (on 64bit & 32bit architectures)
 */
int
getmsb (uint32_t word)
{
    int r = 0;
    if (word < 1)
        return 0;
#ifdef BUILD_64
    union { uint32_t u[2]; double d; } t;  // temp
    t.u[__FLOAT_WORD_ORDER==LITTLE_ENDIAN] = 0x43300000;
    t.u[__FLOAT_WORD_ORDER!=LITTLE_ENDIAN] = word;
    t.d -= 4503599627370496.0;
    r = (t.u[__FLOAT_WORD_ORDER==LITTLE_ENDIAN] >> 20) - 0x3FF;
#else
    while (word >>= 1)
    {
        r++;
    }
#endif  /* BUILD_64 */
    return r;
}

इंट आर नहीं था; मूल रूप से #ifdef BUILD_64ध्वज के ऊपर परिभाषित किया गया है ? जिस स्थिति में उसे सशर्त के भीतर पुनर्वितरण की आवश्यकता नहीं होगी।
डेविड सी। रंकिन

3

क्रमिक सन्निकटन का उपयोग करके C में एक संस्करण:

unsigned int getMsb(unsigned int n)
{
  unsigned int msb  = sizeof(n) * 4;
  unsigned int step = msb;
  while (step > 1)
 {
    step /=2;
    if (n>>msb)
     msb += step;
   else
     msb -= step;
 }
  if (n>>msb)
    msb++;
  return (msb - 1);
}

लाभ: रनिंग टाइम प्रदान की गई संख्या की परवाह किए बिना स्थिर है, क्योंकि लूप की संख्या हमेशा समान होती है। ("अप्रयुक्त int" का उपयोग करते समय 4 लूप)


यदि आप इसे एक टर्नरी ऑपरेटर ( msb += (n>>msb) ? step : -step;) के साथ लिखते हैं , तो अधिक संकलक को शाखा रहित बनाने की संभावना है, हर कदम पर शाखा की गलतफहमी से बचने ( stackoverflow.com/questions/11227809/… )।
पीटर कॉर्डेस

3

मुझे पता है कि यह प्रश्न बहुत पुराना है, लेकिन केवल एक msb () फ़ंक्शन को स्वयं कार्यान्वित करने के बाद , मैंने पाया कि यहां और अन्य वेबसाइटों पर प्रस्तुत अधिकांश समाधान आवश्यक रूप से सबसे अधिक कुशल नहीं हैं - कम से कम दक्षता की मेरी व्यक्तिगत परिभाषा के लिए ( अपडेट भी देखें) नीचे देखें) )। यहाँ पर क्यों:

अधिकांश समाधान (विशेष रूप से जो कुछ प्रकार के द्विआधारी खोज योजना को नियोजित करते हैं या भोले दृष्टिकोण जो दाएं से बाएं एक रैखिक स्कैन करते हैं) इस तथ्य की उपेक्षा करते हैं कि मनमाने ढंग से द्विआधारी संख्याओं के लिए, बहुत से अनुक्रम के साथ शुरू नहीं होते हैं शून्य। वास्तव में, किसी भी बिट-चौड़ाई के लिए, सभी पूर्णांकों में से आधे 1 से शुरू होते हैं और उनमें से एक चौथाई 01 से शुरू होता है । देखो मैं कहाँ पर हूँ? मेरा तर्क है कि एक रैखिक स्कैन सबसे महत्वपूर्ण बिट स्थिति से कम से कम महत्वपूर्ण (बाएं से दाएं) से शुरू होने वाला इतना "रैखिक" नहीं है क्योंकि यह पहली नज़र में लग सकता है।

यह 1 दिखाया जा सकता है , कि किसी भी बिट-चौड़ाई के लिए, बिट्स की औसत संख्या जिसे जांचने की आवश्यकता होती है, वह अधिकतम 2 पर होती है। यह O (1) के एक परिशोधित समय जटिलता में अनुवाद करता है। बिट्स की संख्या के संबंध ! ।

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

यहाँ "भोली" दृष्टिकोण है, जो मेरे साथ आया है, जो कम से कम मेरी मशीन पर अधिकांश अन्य दृष्टिकोणों को धड़कता है (32-बिट इनट्स के लिए द्विआधारी खोज योजनाओं को हमेशा लॉग 2 (32) = 5 चरणों की आवश्यकता होती है, जबकि इस मूर्खतापूर्ण एल्गोरिथ्म को कम की आवश्यकता होती है) औसत से 2) - इसके लिए खेद है C ++ और शुद्ध नहीं C:

template <typename T>
auto msb(T n) -> int
{
    static_assert(std::is_integral<T>::value && !std::is_signed<T>::value,
        "msb<T>(): T must be an unsigned integral type.");

    for (T i = std::numeric_limits<T>::digits - 1, mask = 1 << i; i >= 0; --i, mask >>= 1)
    {
        if ((n & mask) != 0)
            return i;
    }

    return 0;
}

अद्यतन : जबकि मैंने यहां जो लिखा है वह पूरी तरह से मनमाने ढंग से पूर्णांकों केलिए सच है, जहां बिट्स के प्रत्येक संयोजन समान रूप से संभावित है (मेरी गति परीक्षण बस मापा गया कि सभी 32-बिट पूर्णांकों केलिए एमएसबी निर्धारित करने में कितना समय लगा), वास्तविक जीवन पूर्णांक, के लिए इस तरह के एक फ़ंक्शन को बुलाया जाएगा, आमतौर पर एक अलग पैटर्न का पालन करें: मेरे कोड में, उदाहरण के लिए, इस फ़ंक्शन का उपयोग यह निर्धारित करने के लिए किया जाता है कि ऑब्जेक्ट का आकार 2 की शक्ति है, या 2 की अगली शक्ति का पता लगाने के लिए एक से अधिक या बराबर है वस्तु का आकार । मेरा अनुमान है कि MSB का उपयोग करने वाले अधिकांश एप्लिकेशन संख्याओं को शामिल करते हैं जो एक पूर्णांक की अधिकतम संख्या की तुलना में बहुत छोटे होते हैं (ऑब्जेक्ट आकार शायद ही कभी सभी बिट्स का उपयोग size_t में करते हैं।)। इस मामले में, मेरा समाधान वास्तव में एक द्विआधारी खोज दृष्टिकोण से भी बदतर प्रदर्शन करेगा - इसलिए उत्तरार्द्ध को शायद पसंद किया जाना चाहिए, भले ही मेरा समाधान सभी पूर्णांकों के माध्यम से तेजी से लूपिंग होगा ।
टीएल; डीआर: वास्तविक जीवन के पूर्णांकों में संभवतः इस सरल एल्गोरिथ्म के सबसे बुरे मामले की ओर एक पूर्वाग्रह होगा, जो इसे अंत में बदतर प्रदर्शन करेगा - इस तथ्य के बावजूद कि यह वास्तव में मनमाना पूर्णांकों के लिए ओ (1) amortized है

1 तर्क यह (किसी न किसी मसौदा) प्रकार है: Let n बिट्स (बिट-चौड़ाई) की संख्या हो। कुल 2 n पूर्णांक होते हैं, जिन्हें n बिट्स के साथ दर्शाया जा सकता है । कर रहे हैं 2 n - 1 पूर्णांकों एक साथ शुरू 1 (पहले 1 तय हो गई है, शेष n - 1 बिट्स कुछ भी हो सकता है)। उन पूर्णांकों को MSB निर्धारित करने के लिए लूप के केवल एक हस्तक्षेप की आवश्यकता होती है। इसके अलावा, 01 से शुरू होने वाले 2 एन - 2 पूर्णांक हैं , जिसमें 2 पुनरावृत्तियों की आवश्यकता है, 2 एन - 3 पूर्णांक 001 से शुरू होते हैं , जिसमें 3 पुनरावृत्तियों की आवश्यकता होती है, और इसी तरह।

यदि हम सभी संभव पूर्णांकों के लिए सभी आवश्यक पुनरावृत्तियों को जोड़ते हैं और उन्हें 2 n से विभाजित करते हैं, तो पूर्णांकों की कुल संख्या, हमें n- बिट पूर्णांकों के लिए MSB निर्धारित करने के लिए आवश्यक पुनरावृत्तियों की औसत संख्या प्राप्त होती है :

(1 * 2 एन - 1 + 2 * 2 एन - 2 + 3 * 2 एन - 3 + ... + एन) / 2 एन

औसत पुनरावृत्तियों की यह श्रृंखला वास्तव में अभिसरण है और अनंत की ओर n के लिए 2 की सीमा है

इस प्रकार, भोले बाएं से दाएं एल्गोरिथ्म वास्तव में किसी भी संख्या में बिट्स के लिए O (1) की एक परिमित निरंतर समय जटिलता है ।


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

3

हमें दिया है log2। यह log2इस पृष्ठ पर आपके द्वारा देखे जाने वाले सभी विशेष सॉस कार्यान्वयन की आवश्यकता को हटा देता है । आप मानक के log2कार्यान्वयन का उपयोग इस तरह कर सकते हैं :

const auto n = 13UL;
const auto Index = (unsigned long)log2(n);

printf("MSB is: %u\n", Index); // Prints 3 (zero offset)

एक nकी 0ULजरूरत है और साथ ही के खिलाफ क्योंकि पहरा होने के लिए,:

-_ वापस आ गया है और FE_DIVBYZERO को उठाया गया है

मैंने उस जाँच के साथ एक उदाहरण लिखा है कि मनमाने ढंग Indexसे ULONG_MAXयहाँ सेट होता है: https://ideone.com/u26vsi


केवल गपशप करने के लिए गणक का उत्तर है:

const auto n = 13UL;
unsigned long Index;

_BitScanReverse(&Index, n);
printf("MSB is: %u\n", Index); // Prints 3 (zero offset)

_BitScanReverseराज्यों के लिए प्रलेखन जो Indexहै:

पहले सेट बिट (1) की बिट स्थिति के साथ भरा हुआ पाया

अभ्यास में मैंने पाया है कि अगर nहै 0ULकि Indexकरने के लिए सेट कर दिया जाता0UL है, बस के रूप में यह एक के लिए किया जाएगा nकी 1UL। लेकिन केवल एक चीज एक के मामले में दस्तावेज में गारंटी nकी 0ULकि वापसी है:

0 अगर कोई सेट बिट्स नहीं मिला

इस प्रकार, log2रिटर्न के ऊपर बेहतर कार्यान्वयन के समान, Indexइस मामले में एक ध्वजांकित मूल्य पर सेटिंग की जांच की जानी चाहिए । मैंने फिर से ULONG_MAXयहाँ इस ध्वज मूल्य के लिए उपयोग करने का एक उदाहरण लिखा है: http://rextester.com/GCU61409


नहीं, केवल_BitScanReverse 0 रिटर्न अगर इनपुट था । यह x86 के निर्देश की तरह है , जो केवल इनपुट के आधार पर ZF सेट करता है, आउटपुट नहीं। दिलचस्प है कि जब कोई बिट नहीं मिलता है, तो एमएस डॉक्स को अशांत छोड़ देता है; कि x86 asm व्यवहार से भी मेल खाता है। (AMD दस्तावेज इसे src = 0 पर गंतव्य रजिस्टर को अनमॉडिफ़ाइड छोड़ने के रूप में दर्ज करता है, लेकिन इंटेल अभी भी अपरिभाषित आउटपुट कहता है, भले ही उनके सीपीयू छुट्टी-अनमॉडिफ़ाइड व्यवहार को लागू करते हैं।) यह x86 के विपरीत है , जो कि बिना पाए नहीं मिलता है। 0BSRindex1bsrlzcnt32
पीटर कॉर्डेस

@PeterCordes _BitScanReverseशून्य-आधारित अनुक्रमणिका का उपयोग करता है, इस प्रकार यदि n1 है तो सेट बिट का सूचकांक वास्तव में 0. है। दुर्भाग्य से, जैसा कि आप कहते हैं कि यदि n0 है, तो आउटपुट भी 0 है :( इसका मतलब यह है कि वापसी का उपयोग करने का कोई तरीका नहीं है। n1 या 0. के बीच अंतर करें । यही मैं संवाद करने की कोशिश कर रहा था। क्या आपको लगता है कि यह कहने का एक बेहतर तरीका है?
जोनाथन मी

मुझे लगता है कि आप इसके बारे में बात कर रहे हैं कि यह कैसे सेट होता है Index। वह रिटर्न वैल्यू नहीं है। यह एक बूलियन देता है जो कि झूठा है यदि इनपुट शून्य था (और यही कारण है कि सूचकांक सामान्य रूप से वापस जाने के बजाय संदर्भ द्वारा पारित किया जाता है)। godbolt.org/g/gQKJdE । और मैंने जाँच की: एमएस के डॉक्स के शब्दों के बावजूद, _BitScanReverseइंडेक्स को छोड़ना नहीं छोड़ता है n==0: आपको रजिस्टर में जो भी मूल्य का उपयोग करने के लिए हुआ था, वह आपको मिल जाएगा। (आपके मामले में जो संभवत: वही रजिस्टर था जो Indexबाद में इसके लिए इस्तेमाल किया गया था, जिससे आप देखते हैं 0)।
पीटर कॉर्ड्स

यह प्रश्न c ++ का टैग नहीं है।
टेक्नोसॉरस

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

2

बिटवाइज ऑपरेटर्स सोचें।

मुझे पहली बार सवाल याद आया। आपको सबसे बाएं बिट सेट (अन्य शून्य) के साथ एक इंट का उत्पादन करना चाहिए। मान लेना cmp उस मान पर सेट है:

position = sizeof(int)*8
while(!(n & cmp)){ 
   n <<=1;
   position--;
}

क्या आप एक स्ट्रिंग में बदलने का मतलब है? एफएफएस की परिभाषा एक इंट लेती है और एक इंट रिटर्न देती है। रूपांतरण कहां होगा? और अगर हम एक शब्द में बिट्स की तलाश कर रहे हैं तो रूपांतरण किस उद्देश्य से होगा?
ड्रीमलैक्स

मुझे उस फ़ंक्शन का पता नहीं था।
वासिल

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

2

जोश के बेंचमार्क पर विस्तार ... एक क्लॉज में सुधार कर सकता है

/***************** clz2 ********************/

#define NUM_OF_HIGHESTBITclz2(a) ((a)                              \
                  ? (((1U) << (sizeof(unsigned)*8-1)) >> __builtin_clz(a)) \
                  : 0)

Asm के बारे में: ध्यान दें कि bsr और bsrl हैं (यह "लंबा" संस्करण है)। सामान्य थोड़ा तेज हो सकता है।


1

ध्यान दें कि आप जो करने की कोशिश कर रहे हैं, वह पूर्णांक के पूर्णांक log2 की गणना करता है,

#include <stdio.h>
#include <stdlib.h>

unsigned int
Log2(unsigned long x)
{
    unsigned long n = x;
    int bits = sizeof(x)*8;
    int step = 1; int k=0;
    for( step = 1; step < bits; ) {
        n |= (n >> step);
        step *= 2; ++k;
    }
    //printf("%ld %ld\n",x, (x - (n >> 1)) );
    return(x - (n >> 1));
}

निरीक्षण करें कि आप एक बार में 1 बिट से अधिक खोज करने का प्रयास कर सकते हैं।

unsigned int
Log2_a(unsigned long x)
{
    unsigned long n = x;
    int bits = sizeof(x)*8;
    int step = 1;
    int step2 = 0;
    //observe that you can move 8 bits at a time, and there is a pattern...
    //if( x>1<<step2+8 ) { step2+=8;
        //if( x>1<<step2+8 ) { step2+=8;
            //if( x>1<<step2+8 ) { step2+=8;
            //}
        //}
    //}
    for( step2=0; x>1L<<step2+8; ) {
        step2+=8;
    }
    //printf("step2 %d\n",step2);
    for( step = 0; x>1L<<(step+step2); ) {
        step+=1;
        //printf("step %d\n",step+step2);
    }
    printf("log2(%ld) %d\n",x,step+step2);
    return(step+step2);
}

यह दृष्टिकोण एक द्विआधारी खोज का उपयोग करता है

unsigned int
Log2_b(unsigned long x)
{
    unsigned long n = x;
    unsigned int bits = sizeof(x)*8;
    unsigned int hbit = bits-1;
    unsigned int lbit = 0;
    unsigned long guess = bits/2;
    int found = 0;

    while ( hbit-lbit>1 ) {
        //printf("log2(%ld) %d<%d<%d\n",x,lbit,guess,hbit);
        //when value between guess..lbit
        if( (x<=(1L<<guess)) ) {
           //printf("%ld < 1<<%d %ld\n",x,guess,1L<<guess);
            hbit=guess;
            guess=(hbit+lbit)/2;
            //printf("log2(%ld) %d<%d<%d\n",x,lbit,guess,hbit);
        }
        //when value between hbit..guess
        //else
        if( (x>(1L<<guess)) ) {
            //printf("%ld > 1<<%d %ld\n",x,guess,1L<<guess);
            lbit=guess;
            guess=(hbit+lbit)/2;
            //printf("log2(%ld) %d<%d<%d\n",x,lbit,guess,hbit);
        }
    }
    if( (x>(1L<<guess)) ) ++guess;
    printf("log2(x%ld)=r%d\n",x,guess);
    return(guess);
}

एक और द्विआधारी खोज विधि, शायद अधिक पठनीय,

unsigned int
Log2_c(unsigned long x)
{
    unsigned long v = x;
    unsigned int bits = sizeof(x)*8;
    unsigned int step = bits;
    unsigned int res = 0;
    for( step = bits/2; step>0; )
    {
        //printf("log2(%ld) v %d >> step %d = %ld\n",x,v,step,v>>step);
        while ( v>>step ) {
            v>>=step;
            res+=step;
            //printf("log2(%ld) step %d res %d v>>step %ld\n",x,step,res,v);
        }
        step /= 2;
    }
    if( (x>(1L<<res)) ) ++res;
    printf("log2(x%ld)=r%ld\n",x,res);
    return(res);
}

और क्योंकि आप इनका परीक्षण करना चाहेंगे,

int main()
{
    unsigned long int x = 3;
    for( x=2; x<1000000000; x*=2 ) {
        //printf("x %ld, x+1 %ld, log2(x+1) %d\n",x,x+1,Log2(x+1));
        printf("x %ld, x+1 %ld, log2_a(x+1) %d\n",x,x+1,Log2_a(x+1));
        printf("x %ld, x+1 %ld, log2_b(x+1) %d\n",x,x+1,Log2_b(x+1));
        printf("x %ld, x+1 %ld, log2_c(x+1) %d\n",x,x+1,Log2_c(x+1));
    }
    return(0);
}

1

यह डाल के बाद से यह 'अभी तक एक और' दृष्टिकोण है, पहले से दिए गए दूसरों से अलग लगता है।

-1यदि रिटर्न x==0, अन्यथा floor( log2(x)) (अधिकतम परिणाम 31)

32 से 4 बिट समस्या को कम करें, फिर एक तालिका का उपयोग करें। शायद अयोग्य, लेकिन व्यावहारिक।

__builtin_clzपोर्टेबिलिटी की समस्या के कारण जब मैं उपयोग नहीं करना चाहता तो यही है ।

इसे और अधिक कॉम्पैक्ट बनाने के लिए, एक को कम करने के लिए लूप का उपयोग किया जा सकता है, प्रत्येक समय 4 से जोड़कर, अधिकतम 7 पुनरावृत्तियों। या कुछ हाइब्रिड, जैसे कि (64 बिट्स के लिए): 8 को कम करने के लिए लूप, 4 को कम करने के लिए परीक्षण।

int log2floor( unsigned x ){
   static const signed char wtab[16] = {-1,0,1,1, 2,2,2,2, 3,3,3,3,3,3,3,3};
   int r = 0;
   unsigned xk = x >> 16;
   if( xk != 0 ){
       r = 16;
       x = xk;
   }
   // x is 0 .. 0xFFFF
   xk = x >> 8;
   if( xk != 0){
       r += 8;
       x = xk;
   }
   // x is 0 .. 0xFF
   xk = x >> 4;
   if( xk != 0){
       r += 4;
       x = xk;
   }
   // now x is 0..15; x=0 only if originally zero.
   return r + wtab[x];
}

1

Woaw, कि कई जवाब था। मुझे एक पुराने सवाल पर जवाब देने के लिए खेद नहीं है।

int result = 0;//could be a char or int8_t instead
if(value){//this assumes the value is 64bit
    if(0xFFFFFFFF00000000&value){  value>>=(1<<5); result|=(1<<5);  }//if it is 32bit then remove this line
    if(0x00000000FFFF0000&value){  value>>=(1<<4); result|=(1<<4);  }//and remove the 32msb
    if(0x000000000000FF00&value){  value>>=(1<<3); result|=(1<<3);  }
    if(0x00000000000000F0&value){  value>>=(1<<2); result|=(1<<2);  }
    if(0x000000000000000C&value){  value>>=(1<<1); result|=(1<<1);  }
    if(0x0000000000000002&value){  result|=(1<<0);  }
}else{
  result=-1;
}

यह जवाब एक और जवाब के समान है ... ओह ठीक है।


शिफ्ट राशियों को लिखना 1<<kएक अच्छा स्पर्श है। मास्क के बारे में क्या? (1 << (1<<k-1)-1<< (1<<k-1)? ( most optimal? आप एक अतिशयोक्ति की तुलना करते हैं?)
ग्रेबियर्ड

@greybeard यदि आप इस प्रश्न के संपादन को देखते हैं तो आप देखेंगे कि मैंने "इष्टतम" भाग जोड़ा है। मैं अपना जवाब बदलते ही इसे हटाना भूल गया। इसके अलावा, मुझे यकीन नहीं है कि आप मास्क के बारे में क्यों बात कर रहे हैं ? (क्या मास्क? मैं आपका पीछा नहीं कर रहा हूं)
हैरी स्वेन्सन

( (बिट) मुखौटा चयन करने के लिए / साफ बिट्स चुनिंदा / में प्रयोग किया जाता इस्तेमाल किया मान हैं &और &~।) आप की पसंद से हेक्स स्थिरांक की जगह सकता है ((type)1<<(1<<k))-1<<(1<<k)
ग्रेबियर्ड

ओह ठीक है, मैं मास्क का उपयोग कर रहा हूं, मैं इसके बारे में पूरी तरह से भूल गया। मैंने इसका उत्तर कुछ महीने पहले दिया था ... - हम्म, चूंकि इसका संकलन समय के दौरान मूल्यांकन किया गया है, इसलिए मैं कहता हूं कि यह हेक्स मानों के बराबर है। हालांकि, एक गुप्त है और एक हेक्साडेसिमल है।
हैरी स्वेन्सन

0

कोड:

    // x>=1;
    unsigned func(unsigned x) {
    double d = x ;
    int p= (*reinterpret_cast<long long*>(&d) >> 52) - 1023;
    printf( "The left-most non zero bit of %d is bit %d\n", x, p);
    }

या Y = 1 सेट करके FPU अनुदेश FYL2X (Y * Log2 X) का पूर्णांक भाग प्राप्त करें


uhhhhh। क्या? यह कैसे कार्य करता है? क्या यह किसी भी तरह से पोर्टेबल है?
अंडरस्कोर_ड

विंडो में कोड पोर्टेबल है। फ़ंक्शन FYL2X () एक fpu निर्देश है, लेकिन इसे पोर्ट किया जा सकता है और कुछ FPU / गणित पुस्तकालय में पाया जा सकता है।
जेमिन

@underscore_d यह काम करता है क्योंकि फ्लोटिंग पॉइंट नंबर सामान्य किए जाते हैं ... अग्रणी शून्य को खत्म करने के लिए मेंटिसा बिट्स को डबल शिफ्ट में परिवर्तित करता है, और यह कोड एक्सप्रैक्टर को निकालता है और बिट्स शिफ्ट की संख्या निर्धारित करने के लिए इसे समायोजित करता है। यह निश्चित रूप से वास्तुकला-स्वतंत्र नहीं है, लेकिन यह संभवतः आपके द्वारा भर में आने वाली किसी भी मशीन पर काम करेगा।
जिम बेल्टर

यह इस उत्तर का एक वैकल्पिक संस्करण है , प्रदर्शन और पोर्टेबिलिटी पर टिप्पणियों के लिए वहां देखें। (विशेष रूप से टाइप-पाइंटिंग के लिए पॉइंटर कास्टिंग की गैर-पोर्टेबिलिटी।) यह केवल उच्च 32 बिट्स को फिर से लोड करने के लिए एड्रेस मैथ का उपयोग करता है double, जो कि वास्तव में अच्छा है यदि यह टाइप-पेन के बजाय किसी अन्य तरीके से स्टोर / रीलोड करता है, जैसे। एक movqनिर्देश के साथ जैसे आप x86 पर यहाँ प्राप्त कर सकते हैं।
पीटर कॉर्डेस

मेरे [उस उत्तर पर टिप्पणी] पर भी ध्यान दें, जहां मैं सख्त चेतावनी देता हूं कि यह विधि सीमा में (कम से कम) मूल्यों के लिए गलत उत्तर देती है [7FFFFFFFFFFFFE00 - 7FFFFFFFFFFFFFFF]
ग्लेन स्लेडेन

0

एक अन्य पोस्टर ने बाइट-वाइड लुकअप का उपयोग करके एक लुक-टेबल प्रदान किया । यदि आप कुछ अधिक प्रदर्शन (केवल 256 लुकअप प्रविष्टियों के बजाय 32K मेमोरी की लागत पर) बाहर निकालना चाहते हैं , तो .NET के लिए C # 7 में 15-बिट लुकअप तालिका का उपयोग करके एक समाधान है ।

दिलचस्प हिस्सा तालिका को शुरुआती कर रहा है। चूंकि यह एक अपेक्षाकृत छोटा ब्लॉक है जिसे हम प्रक्रिया के जीवनकाल के लिए चाहते हैं, मैं इसका उपयोग करके इसके लिए अप्रबंधित मेमोरी आवंटित करता हूं Marshal.AllocHGlobal। जैसा कि आप देख सकते हैं, अधिकतम प्रदर्शन के लिए, पूरे उदाहरण को मूल रूप में लिखा गया है:

readonly static byte[] msb_tab_15;

// Initialize a table of 32768 bytes with the bit position (counting from LSB=0)
// of the highest 'set' (non-zero) bit of its corresponding 16-bit index value.
// The table is compressed by half, so use (value >> 1) for indexing.
static MyStaticInit()
{
    var p = new byte[0x8000];

    for (byte n = 0; n < 16; n++)
        for (int c = (1 << n) >> 1, i = 0; i < c; i++)
            p[c + i] = n;

    msb_tab_15 = p;
}

तालिका को ऊपर दिए गए कोड के माध्यम से एक बार के आरंभीकरण की आवश्यकता है। यह केवल पढ़ने योग्य है इसलिए समवर्ती अभिगम के लिए एक ही वैश्विक प्रति साझा की जा सकती है। इस तालिका के साथ आप पूर्णांक लॉग 2 को देख सकते हैं , जो कि हम यहां देख रहे हैं, सभी विभिन्न पूर्णांक चौड़ाई (8, 16, 32 और 64 बिट्स) के लिए।

ध्यान दें कि 0एकमात्र पूर्णांक, जिसके लिए 'उच्चतम सेट बिट' की धारणा अपरिभाषित है, के लिए तालिका प्रविष्टि को मान दिया जाता है -1। यह अंतर नीचे दिए गए कोड में 0-मूल्यवान ऊपरी शब्दों के उचित हैंडलिंग के लिए आवश्यक है। आगे की हलचल के बिना, यहाँ विभिन्न पूर्णांक प्राइमरों में से प्रत्येक के लिए कोड है:

ulong (64-बिट) संस्करण

/// <summary> Index of the highest set bit in 'v', or -1 for value '0' </summary>
public static int HighestOne(this ulong v)
{
    if ((long)v <= 0)
        return (int)((v >> 57) & 0x40) - 1;      // handles cases v==0 and MSB==63

    int j = /**/ (int)((0xFFFFFFFFU - v /****/) >> 58) & 0x20;
    j |= /*****/ (int)((0x0000FFFFU - (v >> j)) >> 59) & 0x10;
    return j + msb_tab_15[v >> (j + 1)];
}

uint (32-बिट) संस्करण

/// <summary> Index of the highest set bit in 'v', or -1 for value '0' </summary>
public static int HighestOne(uint v)
{
    if ((int)v <= 0)
        return (int)((v >> 26) & 0x20) - 1;     // handles cases v==0 and MSB==31

    int j = (int)((0x0000FFFFU - v) >> 27) & 0x10;
    return j + msb_tab_15[v >> (j + 1)];
}

उपरोक्त के लिए विभिन्न अधिभार

public static int HighestOne(long v) => HighestOne((ulong)v);
public static int HighestOne(int v) => HighestOne((uint)v);
public static int HighestOne(ushort v) => msb_tab_15[v >> 1];
public static int HighestOne(short v) => msb_tab_15[(ushort)v >> 1];
public static int HighestOne(char ch) => msb_tab_15[ch >> 1];
public static int HighestOne(sbyte v) => msb_tab_15[(byte)v >> 1];
public static int HighestOne(byte v) => msb_tab_15[v >> 1];

यह एक संपूर्ण, कार्यशील समाधान है, जो कि एक विशेष प्रदर्शन परीक्षण दोहन के साथ तुलना में कई विकल्पों के लिए .NET 4.7.2 पर सर्वश्रेष्ठ प्रदर्शन का प्रतिनिधित्व करता है। इनमें से कुछ का उल्लेख नीचे किया गया है। परीक्षण पैरामीटर सभी 65 बिट पदों के एक समान घनत्व थे, अर्थात, 0 ... 31/63 प्लस मूल्य 0(जो परिणाम -1 का उत्पादन करता है)। लक्ष्य सूचकांक स्थिति के नीचे के बिट अनियमित रूप से भरे गए थे। परीक्षण केवल x64 थे , रिलीज़ मोड, जिसमें JIT- अनुकूलन सक्षम थे।




यहाँ मेरे औपचारिक जवाब का अंत है; इस परीक्षण से जुड़े वैकल्पिक परीक्षण के उम्मीदवारों के लिए कुछ आकस्मिक नोट्स और स्रोत कोड के लिंक निम्न प्रकार हैं जो उपरोक्त कोड के प्रदर्शन और शुद्धता को सत्यापित करने के लिए दौड़े।


ऊपर दिया गया संस्करण, Tab16A के रूप में कोडित किया गया था जो कई रनों पर एक सुसंगत विजेता था। ये विभिन्न उम्मीदवारों, सक्रिय काम कर रहे / खरोंच के रूप में, पाया जा सकता है यहां , यहां , और यहां

 1 अभ्यर्थी। HighestOne_Tab16A 622,496
 2 उम्मीदवार। HighestOne_Tab16C 628,234
 3 उम्मीदवार। HighestOne_Tab8A 649,146
 4 उम्मीदवार। HighestOne_Tab8B 656,847
 5 उम्मीदवार। HighestOne_Tab16B 657,147
 6 उम्मीदवार। HighestOne_Tab16D 659,650
 7 _highest_one_bit_UNMANAGED.HighestOne_U 702,900
 8 de_Bruijn.IndexOfMSB 709,672
 9 _old_2.HighestOne_Old2 715,810
10 _test_A.HighestOne8 757,188
11 _old_1.HighestOne_Old1 757,925
12 _test_A.HighestOne5 (असुरक्षित) 760,387
13 _test_B.HighestOne8 (असुरक्षित) 763,904
14 _test_A.HighestOne3 (असुरक्षित) 766,433
15 _test_A.HighestOne1 (असुरक्षित) 767,321
16 _test_A.HighestOne4 (असुरक्षित) 771,702
17 _test_B.HighestOne2 (असुरक्षित) 772,136
18 _test_B.HighestOne1 (असुरक्षित) 772,527
19 _test_B.HighestOne3 (असुरक्षित) 774,140
20 _test_A.HighestOne7 (असुरक्षित) 774,581
21 _test_B.HighestOne7 (असुरक्षित) 775,463
22 _test_A.HighestOne2 (असुरक्षित) 776,865
23 उम्मीदवार। HighestOne_NoTab 777,698
24 _test_B.HighestOne6 (असुरक्षित) 779,481
25 _test_A.HighestOne6 (असुरक्षित) 781,553
26 _test_B.HighestOne4 (असुरक्षित) 785,504
27 _test_B.HighestOne5 (असुरक्षित) 789,797
28 _test_A.HighestOne0 (असुरक्षित) 809,566
29 _test_B.HighestOne0 (असुरक्षित) 814,990
30 _highest_one_bit.ighestOne 824,345
30 _bitarray_ext.RtlFindMostSignificantBit 894,069
31 उम्मीदवार। HighestOne_Naive 898,865

उल्लेखनीय है कि ntdll.dll!RtlFindMostSignificantBitP / Invoke के माध्यम से भयानक प्रदर्शन :

[DllImport("ntdll.dll"), SuppressUnmanagedCodeSecurity, SecuritySafeCritical]
public static extern int RtlFindMostSignificantBit(ulong ul);

यह वास्तव में बहुत बुरा है, क्योंकि यहां संपूर्ण वास्तविक कार्य है:

    RtlFindMostSignificantBit:
        bsr rdx, rcx  
        mov eax,0FFFFFFFFh  
        movzx ecx, dl  
        cmovne      eax,ecx  
        ret

मैं इन पांच लाइनों के साथ होने वाले खराब प्रदर्शन की कल्पना नहीं कर सकता, इसलिए प्रबंधित / देशी संक्रमण दंड को दोष देना होगा। मुझे यह भी आश्चर्य हुआ कि परीक्षण ने वास्तव shortमें 128-बाइट (और 256-बाइट) byte(8-बिट) लुकअप टेबल पर 32KB (और 64KB) (16-बिट) प्रत्यक्ष-लुकअप तालिकाओं का पक्ष लिया । मैंने सोचा था कि निम्नलिखित 16-बिट लुकअप के साथ अधिक प्रतिस्पर्धी होगा, लेकिन बाद वाले ने लगातार इसे बेहतर बनाया:

public static int HighestOne_Tab8A(ulong v)
{
    if ((long)v <= 0)
        return (int)((v >> 57) & 64) - 1;

    int j;
    j =  /**/ (int)((0xFFFFFFFFU - v) >> 58) & 32;
    j += /**/ (int)((0x0000FFFFU - (v >> j)) >> 59) & 16;
    j += /**/ (int)((0x000000FFU - (v >> j)) >> 60) & 8;
    return j + msb_tab_8[v >> j];
}

आखिरी बात जो मैं बताऊंगा वह यह कि मैं काफी हैरान था कि मेरा डीब्रिजन तरीका बेहतर नहीं था। यह वह विधि है जिसका मैं पहले भी व्यापक रूप से उपयोग कर रहा था:

const ulong N_bsf64 = 0x07EDD5E59A4E28C2,
            N_bsr64 = 0x03F79D71B4CB0A89;

readonly public static sbyte[]
bsf64 =
{
    63,  0, 58,  1, 59, 47, 53,  2, 60, 39, 48, 27, 54, 33, 42,  3,
    61, 51, 37, 40, 49, 18, 28, 20, 55, 30, 34, 11, 43, 14, 22,  4,
    62, 57, 46, 52, 38, 26, 32, 41, 50, 36, 17, 19, 29, 10, 13, 21,
    56, 45, 25, 31, 35, 16,  9, 12, 44, 24, 15,  8, 23,  7,  6,  5,
},
bsr64 =
{
     0, 47,  1, 56, 48, 27,  2, 60, 57, 49, 41, 37, 28, 16,  3, 61,
    54, 58, 35, 52, 50, 42, 21, 44, 38, 32, 29, 23, 17, 11,  4, 62,
    46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43, 31, 22, 10, 45,
    25, 39, 14, 33, 19, 30,  9, 24, 13, 18,  8, 12,  7,  6,  5, 63,
};

public static int IndexOfLSB(ulong v) =>
    v != 0 ? bsf64[((v & (ulong)-(long)v) * N_bsf64) >> 58] : -1;

public static int IndexOfMSB(ulong v)
{
    if ((long)v <= 0)
        return (int)((v >> 57) & 64) - 1;

    v |= v >> 1; v |= v >> 2;  v |= v >> 4;   // does anybody know a better
    v |= v >> 8; v |= v >> 16; v |= v >> 32;  // way than these 12 ops?
    return bsr64[(v * N_bsr64) >> 58];
}

इस SO प्रश्न पर कितने बेहतर और महान deBruijn तरीकों की चर्चा है , और मैं सहमत था। मेरी अटकलें यह है कि, जबकि डेब्रिज और डायरेक्ट लुकअप टेबल मेथड (जो मुझे सबसे तेज़ लगे) दोनों में एक टेबल लुकअप करना होता है, और दोनों में बहुत कम ब्रांचिंग होती है, केवल डेब्रिज में 64-बिट मल्टीप्ल ऑपरेशन होता है। मैंने केवल IndexOfMSBयहाँ फ़ंक्शन का परीक्षण किया है - न कि deBruijn IndexOfLSB- लेकिन मैं उम्मीद करता हूं कि उत्तरार्द्ध बेहतर मौका का मौका देगा क्योंकि इसमें बहुत कम ऑपरेशन हैं (ऊपर देखें), और मैं संभवतः इसे एलएसबी के लिए उपयोग करना जारी रखूंगा।


1
आधुनिक x86 CPUs पर L1D कैश केवल 32kiB है। जब तक आप एक ही मान बार-बार उपयोग नहीं कर रहे हैं, तब तक एक बड़ी LUT एक छोटी LUT से भी बदतर होने की संभावना है। यदि आप नहीं हैं, तो आपको लगातार कैशे छूट जाएंगे।
पीटर कॉर्ड्स

0

मेरी विनम्र विधि बहुत सरल है:

MSB (x) = INT [लॉग (x) / लॉग (2)]

अनुवाद: x का MSB पूर्णांक मान (बेस 2 के लॉग द्वारा विभाजित x का आधार) है।

यह आसानी से और जल्दी से किसी भी प्रोग्रामिंग भाषा के लिए अनुकूलित किया जा सकता है। अपने आप को देखने के लिए अपने कैलकुलेटर पर कोशिश करें कि यह काम करता है।


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

यह राउंडऑफ़ त्रुटि के कारण विफल हो सकता है। उदाहरण के लिए, CPython 2 और 3 में, int(math.log((1 << 48) - 1) / math.log(2))48 है।
benrg

0

यहाँ C के लिए एक तेज़ समाधान है जो GCC और Clang में काम करता है ; कॉपी और पेस्ट करने के लिए तैयार है।

#include <limits.h>

unsigned int fls(const unsigned int value)
{
    return (unsigned int)1 << ((sizeof(unsigned int) * CHAR_BIT) - __builtin_clz(value) - 1);
}

unsigned long flsl(const unsigned long value)
{
    return (unsigned long)1 << ((sizeof(unsigned long) * CHAR_BIT) - __builtin_clzl(value) - 1);
}

unsigned long long flsll(const unsigned long long value)
{
    return (unsigned long long)1 << ((sizeof(unsigned long long) * CHAR_BIT) - __builtin_clzll(value) - 1);
}

और C ++ के लिए थोड़ा बेहतर संस्करण ।

#include <climits>

constexpr unsigned int fls(const unsigned int value)
{
    return (unsigned int)1 << ((sizeof(unsigned int) * CHAR_BIT) - __builtin_clz(value) - 1);
}

constexpr unsigned long fls(const unsigned long value)
{
    return (unsigned long)1 << ((sizeof(unsigned long) * CHAR_BIT) - __builtin_clzl(value) - 1);
}

constexpr unsigned long long fls(const unsigned long long value)
{
    return (unsigned long long)1 << ((sizeof(unsigned long long) * CHAR_BIT) - __builtin_clzll(value) - 1);
}

कोड मानता है कि valueनहीं होगा 0। यदि आप 0 को अनुमति देना चाहते हैं, तो आपको इसे संशोधित करने की आवश्यकता है।


0

मुझे लगता है कि आपका प्रश्न एक पूर्णांक (नीचे v) कहा जाता है और अहस्ताक्षरित पूर्णांक नहीं है।

int v = 612635685; // whatever value you wish

unsigned int get_msb(int v)
{
    int r = 31;                         // maximum number of iteration until integer has been totally left shifted out, considering that first bit is index 0. Also we could use (sizeof(int)) << 3 - 1 instead of 31 to make it work on any platform.

    while (!(v & 0x80000000) && r--) {   // mask of the highest bit
        v <<= 1;                        // multiply integer by 2.
    }
    return r;                           // will even return -1 if no bit was set, allowing error catch
}

यदि आप उस खाते पर ध्यान दिए बिना काम करना चाहते हैं जो आप एक अतिरिक्त 'v << = 1;' जोड़ सकते हैं। लूप से पहले (और तदनुसार r मान को 30 पर बदलें)। कृपया मुझे बताएं कि क्या मैं कुछ भी भूल गया हूं। मैंने इसका परीक्षण नहीं किया है, लेकिन यह ठीक काम करना चाहिए।


v <<= 1है अपरिभाषित व्यवहार (यूबी) जब v < 0
chux -

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