web-dev-qa-db-ja.com

ビット配列に設定されている最上位ビット(左端)を見つける

0番目のインデックスが配列の最初のバイトのMSBであり、8番目のインデックスが2番目のバイトのMSBであるビット配列実装を持っています...

このビット配列に設定されている最初のビットをすばやく見つける方法は何ですか?私が調べた関連ソリューションはすべて、最初の最下位ビットを見つけますが、最初の最も重要なビットが必要です。したがって、0x00A1を指定すると、8が必要です(左から9番目のビットなので)。

38
Claudiu

GCCには __builtin_clz これは、x86/x64ではBSR、ARMではCLZなどに変換され、ハードウェアが実装していない場合は命令をエミュレートします。
Visual C++ 2005以降では _BitScanReverse

39
Andras Vass

tl:dr; 32ビットの場合、 de Bruijn乗算を使用します

"fastest" ポータブルアルゴリズムです。このスレッドの他のすべてのポータブル32ビットMSBアルゴリズムよりも大幅に高速で正確です。

De Bruijnアルゴリズムは、入力がゼロの場合にも正しい結果を返します。 __builtin_clzおよび_BitScanReverse命令 誤った結果を返す 入力がゼロの場合

Windows x86-64では、de Bruijn乗算は、同等の(欠陥のある)Windows関数に匹敵する速度で実行されます。パフォーマンスの差はわずか3%です。

コードは次のとおりです。

u32 msbDeBruijn32( u32 v )
{
    static const int MultiplyDeBruijnBitPosition[32] =
    {
        0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
        8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
    };

    v |= v >> 1; // first round down to one less than a power of 2
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;

    return MultiplyDeBruijnBitPosition[( u32 )( v * 0x07C4ACDDU ) >> 27];
}

このスレッドの他のすべての回答は、著者が示唆するよりも実行がはるかに遅いか、結果を正しく計算しないか、またはその両方です。それらすべてのベンチマークを行い、彼らが主張することを実行することを確認しましょう。

これらすべての実装をテストするための簡単なC++ 11ハーネスを次に示します。 Visual Studioで正常にコンパイルされますが、すべての最新のコンパイラで動作するはずです。パフォーマンスモード(bVerifyResults = false)およびチェックモード(bVerifyResults = true)でベンチマークを実行できます。

検証モードでの結果は次のとおりです。

Verification failed for msbNative64: input was 0; output was 818af060; expected 0
Verification failed for msbFfs: input was 22df; output was 0; expected d
Verification failed for msbPerformanceJunkie32: input was 0; output was ffffffff; expected 0
Verification failed for msbNative32: input was 0; output was 9ab07060; expected 0

「パフォーマンスジャンキー」とMicrosoftネイティブ実装は、入力がゼロの場合に異なる処理を実行します。 msbPerformanceJunkie32は-1を生成し、Microsoftの_BitScanReverseは、基になるハードウェア命令と一致する乱数を生成します。また、msbPerformanceJunkie32実装は、他のすべての回答から1つ外れた結果を生成します。

リリースモードでコンパイルされたi7-4600ラップトップで実行されているパフォーマンスモードでの結果は次のとおりです。

msbLoop64 took 2.56751 seconds               
msbNative64 took 0.222197 seconds            

msbLoop32 took 1.43456 seconds               
msbFfs took 0.525097 seconds                 
msbPerformanceJunkie32 took 1.07939 seconds  
msbDeBruijn32 took 0.224947 seconds          
msbNative32 took 0.218275 seconds            

De Bruijnバージョンは、ブランチレスであるため、他の実装soundlyよりも優れているため、均等に分散された出力セットを生成する入力に対して適切に動作します。他のすべてのバージョンは、最新のCPUでの分岐の予測ミスのペナルティのために、任意の入力に対して低速です。 smbFfs関数は誤った結果を生成するため、無視できます。

実装の一部は32ビット入力で動作し、一部は64ビット入力で動作します。テンプレートは、入力サイズに関係なく、リンゴとリンゴを比較するのに役立ちます。

コードは次のとおりです。必要に応じて、ベンチマークをダウンロードして実行してください。

#include <iostream>
#include <chrono>
#include <random>
#include <cassert>
#include <string>
#include <limits>

#ifdef _MSC_VER
#define Microsoft_COMPILER 1
#include <intrin.h>
#endif // _MSC_VER

const int iterations = 100000000;
bool bVerifyResults = false;
std::random_device rd;
std::default_random_engine re(rd());
typedef unsigned int u32;
typedef unsigned long long u64;

class Timer
{
public:
    Timer() : beg_(clock_::now()) {}
    void reset() {
        beg_ = clock_::now();
    }
    double elapsed() const {
        return std::chrono::duration_cast<second_>
            (clock_::now() - beg_).count();
    }

private:
    typedef std::chrono::high_resolution_clock clock_;
    typedef std::chrono::duration<double, std::ratio<1> > second_;
    std::chrono::time_point<clock_> beg_;
};

unsigned int msbPerformanceJunkie32(u32 x)
{
    static const unsigned int bval[] =
    { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 };
    unsigned int r = 0;
    if (x & 0xFFFF0000) {
        r += 16 / 1;
        x >>= 16 / 1;
    }
    if (x & 0x0000FF00) {
        r += 16 / 2;
        x >>= 16 / 2;
    }
    if (x & 0x000000F0) {
        r += 16 / 4;
        x >>= 16 / 4;
    }
    return r + bval[x];
}

#define FFS(t)  \
{ \
register int n = 0; \
if (!(0xffff & t)) \
n += 16; \
if (!((0xff << n) & t)) \
n += 8; \
if (!((0xf << n) & t)) \
n += 4; \
if (!((0x3 << n) & t)) \
n += 2; \
if (!((0x1 << n) & t)) \
n += 1; \
return n; \
}

unsigned int msbFfs32(u32 x)
{
    FFS(x);
}

unsigned int msbLoop32(u32 x)
{
    int r = 0;
    if (x < 1) return 0;
    while (x >>= 1) r++;
    return r;
}

unsigned int msbLoop64(u64 x)
{
    int r = 0;
    if (x < 1) return 0;
    while (x >>= 1) r++;
    return r;
}

u32 msbDeBruijn32(u32 v)
{
    static const int MultiplyDeBruijnBitPosition[32] =
    {
        0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
        8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
    };

    v |= v >> 1; // first round down to one less than a power of 2
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;

    return MultiplyDeBruijnBitPosition[(u32)(v * 0x07C4ACDDU) >> 27];
}

#ifdef Microsoft_COMPILER
u32 msbNative32(u32 val)
{
    unsigned long result;
    _BitScanReverse(&result, val);
    return result;
}
u32 msbNative64(u64 val)
{
    unsigned long result;
    _BitScanReverse64(&result, val);
    return result;
}
#endif // Microsoft_COMPILER

template <typename InputType>
void test(unsigned int msbFunc(InputType),
    const std::string &name,
    const std::vector< InputType > &inputs,
    std::vector< unsigned int > &results,
    bool bIsReference = false
)
{
    if (bIsReference)
    {
        int i = 0;
        for (int i = 0; i < iterations; i++)
            results[i] = msbFunc(inputs[i]);
    }
    InputType result;
    if (bVerifyResults)
    {
        bool bNotified = false;
        for (int i = 0; i < iterations; i++)
        {
            result = msbFunc(inputs[i]);
            if ((result != results[i]) && !bNotified)
            {
                std::cout << "Verification failed for " << name << ": "
                    << "input was " << std::hex << inputs[i]
                    << "; output was " << result
                    << "; expected " << results[i]
                    << std::endl;
                bNotified = true;
            }
        }
    }
    else
    {
        Timer t;
        for (int i = 0; i < iterations; i++)
        {
            result = msbFunc(inputs[i]);
        }
        double elapsed = t.elapsed();
        if ( !bIsReference )
            std::cout << name << " took " << elapsed << " seconds" << std::endl;
        if (result == -1.0f)
            std::cout << "this comparison only exists to keep the compiler from " <<
            "optimizing out the benchmark; this branch will never be called";
    }
}

void main()
{
    std::uniform_int_distribution <u64> dist64(0,
        std::numeric_limits< u64 >::max());
    std::uniform_int_distribution <u32> shift64(0, 63);
    std::vector< u64 > inputs64;
    for (int i = 0; i < iterations; i++)
    {
        inputs64.Push_back(dist64(re) >> shift64(re));
    }
    std::vector< u32 > results64;
    results64.resize(iterations);

    test< u64 >(msbLoop64, "msbLoop64", inputs64, results64, true);
    test< u64 >(msbLoop64, "msbLoop64", inputs64, results64, false);
#ifdef Microsoft_COMPILER
    test< u64 >(msbNative64, "msbNative64", inputs64, results64, false);
#endif // Microsoft_COMPILER
    std::cout << std::endl;

    std::uniform_int_distribution <u32> dist32(0,
        std::numeric_limits< u32 >::max());
    std::uniform_int_distribution <u32> shift32(0, 31);
    std::vector< u32 > inputs32;
    for (int i = 0; i < iterations; i++)
        inputs32.Push_back(dist32(re) >> shift32(re));
    std::vector< u32 > results32;
    results32.resize(iterations);


    test< u32 >(msbLoop32, "msbLoop32", inputs32, results32, true);

    test< u32 >(msbLoop32, "msbLoop32", inputs32, results32, false);
    test< u32 >(msbFfs32, "msbFfs", inputs32, results32, false);
    test< u32 >(msbPerformanceJunkie32, "msbPerformanceJunkie32",
        inputs32, results32, false);
    test< u32 >(msbDeBruijn32, "msbDeBruijn32", inputs32, results32, false);
#ifdef Microsoft_COMPILER
    test< u32 >(msbNative32, "msbNative32", inputs32, results32, false);
#endif // Microsoft_COMPILER
}
23
johnwbyrd

パフォーマンスジャンキーとして、MSBセットのバリエーションを多数試しましたが、次のものが私が遭遇した最速です。

unsigned int msb32(unsigned int x)
{
    static const unsigned int bval[] =
    {0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4};

    unsigned int r = 0;
    if (x & 0xFFFF0000) { r += 16/1; x >>= 16/1; }
    if (x & 0x0000FF00) { r += 16/2; x >>= 16/2; }
    if (x & 0x000000F0) { r += 16/4; x >>= 16/4; }
    return r + bval[x];
}
19
Sir Slick

これを行うには複数の方法があり、異なる実装の相対的なパフォーマンスはある程度マシンに依存します(これを同様の目的である程度ベンチマークしました)。一部のマシンでは、このための組み込みの指示さえあります(使用可能な場合は使用し、移植性に対処できる場合)。

いくつかの実装を確認してください here (「integer log base 2」の下)。 GCCを使用している場合は、関数__builtin_clzおよび__builtin_clzl(ゼロ以外の符号なし整数と符号なしlongに対してそれぞれこれを行います)。 「clz」は「count leading zeros」の略で、同じ問題を説明する別の方法です。

もちろん、ビット配列が適切なマシンWordに収まらない場合、配列内の単語を反復処理して最初の非ゼロのWordを見つけ、そのWordでのみこの計算を実行する必要があります。

12
Arkku

これを行う最も速い方法については、BSR(ビットスキャンリバース)x86 asm命令を参照してください。 Intelのドキュメントから:Searches the source operand (second operand) for the most significant set bit (1 bit). If a most significant 1 bit is found, its bit index is stored in the destination operand (first operand).

5
ggiroux
3
Martin Beckett

X86を使用している場合、SSE2操作と(gccの世界では)「ffs」と発音されるfind-first-bit命令を組み合わせて、事実上、バイト単位またはワード単位のソリューションを破ることができます。 「最下位ビットには「fls」、最上位ビットには「fls」。回答に「C」コードをフォーマットするのに問題があります(!@#$%^)。チェックアウト: http://mischasan.wordpress.com/2011/11/03/sse2-bit-trick-ffsfls-for-xmm-registers/

2
Mischa

最も重要なビットを取得するために多くの関数を使用しましたが、一般に32ビットと64ビットの数値間またはx86_64とx86ボックス間の問題が発生します。関数__builtin_clz__builtin_clzl、および__builtin_clzllは、32/64ビット数およびx86_64およびx86マシンで適切に機能します。ただし、3つの機能が必要です。正数のすべてのケースを処理する右シフトに依存する単純なMSBを見つけました。少なくとも私がそれを使用する場合、他の人が失敗した場合は成功しました:

int
getmsb (unsigned long long x)
{
    int r = 0;
    if (x < 1) return 0;
    while (x >>= 1) r++;
    return r;
}

入力をunsigned long longとして指定することにより、unsigned charからunsigned long longまでのすべての数値クラスを処理でき、標準定義が与えられると、x86_64およびx86ビルド間で互換性があります。 0のケースは0を返すように定義されていますが、必要に応じて変更できます。簡単なテストと出力は次のとおりです。

int
main (int argc, char *argv[]) {

    unsigned char c0 = 0;
    unsigned char c = 216;
    unsigned short s = 1021;
    unsigned int ui = 32768;
    unsigned long ul = 3297381253;
    unsigned long long ull = 323543844043;

    int i = 32767;

    printf ("  %16u  MSB : %d\n", c0, getmsb (c0));
    printf ("  %16u  MSB : %d\n", c, getmsb (c));
    printf ("  %16u  MSB : %d\n", s, getmsb (s));
    printf ("  %16u  MSB : %d\n", i, getmsb (i));
    printf ("  %16u  MSB : %d\n", ui, getmsb (ui));
    printf ("  %16lu  MSB : %d\n", ul, getmsb (ul));
    printf ("  %16llu  MSB : %d\n", ull, getmsb (ull));

    return 0;
}

出力:

             0  MSB : 0
           216  MSB : 7
          1021  MSB : 9
         32767  MSB : 14
         32768  MSB : 15
    3297381253  MSB : 31
  323543844043  MSB : 38

注:速度を考慮して、単一の関数を使用して__builtin_clzllを中心とする同じことを達成すると、約6倍速くなります。

2
David C. Rankin

追加します!

typedef unsigned long long u64;
typedef unsigned int       u32;
typedef unsigned char      u8;


u8 findMostSignificantBit (u64 u64Val)
{
  u8 u8Shift;
  u8 u8Bit = 0;

  assert (u64Val != 0ULL);

  for (u8Shift = 32 ; u8Shift != 0 ; u8Shift >>= 1)
  {
    u64 u64Temp = u64Val >> u8Shift;
    if (u64Temp)
    {
      u8Bit |= u8Shift; // notice not using +=
      u64Val = u64Temp;
    }
  }

  return u8Bit;
}

もちろん、これは配列ではなく64ビットの数値(unsigned long long)で機能しています。また、私が知らなかった組み込みのg ++​​関数を多くの人が指摘しています。なんて面白い。

とにかく、これは6回の反復で最上位ビットを見つけ、関数に0を渡した場合にアサートを与えます。チップセットの命令にアクセスできる場合、使用するのに最適な機能ではありません。

また、+ =の代わりに| =を使用しています。これらは常に2のべき乗であり、ORは(古典的に)加算よりも高速です。ロールオーバーしたことはありません。

これはバイナリ検索であり、常に6回の反復で結果を見つけます。

繰り返しますが、これは優れています:

u8 findMostSignificantBit2 (u64 u64Val)
{
  assert (u64Val != 0ULL);

  return (u8) (__builtin_ctzll(u64Val));
}
1
Richard wicks

最速ではありませんが、動作します...

//// C program
#include <math.h>

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

#define NUM_OF_HIGHESTBIT(a) ((!(a))          \
        ? 0 /* no msb set*/                   \
        : (1 << POS_OF_HIGHESTBIT(a) ))
// could be changed and optimized, if it is known that the following NEVER holds: a <= 0



int main()
{
  unsigned a = 5; // 0b101
  unsigned b = NUM_OF_HIGHESTBIT(a); // 4 since 4 = 0b100
  return 0; 
}
1
Jeff

x86には、ビットインデックス(先行ゼロのカウントaboveではなく)を返すBSR命令があります。

しかし、残念ながら、効率的にすべてのコンパイラーに公開する移植可能な組み込み関数はありません。 GNU Cは___builtin_clz_を提供しますが、unsigned bitidx = 31 - __builtin_clz(x);は現在のGCCおよびICCを使用してBSRに最適化することはしません。式は同等なので、could)。


以下は、x86でbsr命令をjustに効率的にコンパイルするBSR32()およびBSR64()マクロまたは関数を定義しています。 (入力がゼロの場合、ガベージ結果が生成されます。組み込み関数では、asm命令の動作を利用して、input = 0の宛先を変更しないままにする方法はありません。)

非x86への移植性には、さらに_#ifdef_が必要です。 _31-__builtin_clz_にフォールバックします。ほとんどの非x86 ISAは、先行ゼロビットがある場合、ビットインデックスを提供する代わりに先行ゼロをカウントします。そのため、GNU Cは___builtin_clz_をポータブルビルトインとして定義します。(ターゲットシステムにHWサポートがない場合、ビルトインは通常libgccヘルパー関数を呼び出してソフトウェアエミュレーションにコンパイルします。 )

_#include <stdint.h>

// define BSR32() and BSR64()
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
    #ifdef __INTEL_COMPILER
        typedef unsigned int bsr_idx_t;
    #else
        #include <intrin.h>   // MSVC
        typedef unsigned long bsr_idx_t;
    #endif

    static inline
    unsigned BSR32(unsigned long x){
        bsr_idx_t idx;
        _BitScanReverse(&idx, x); // ignore bool retval
        return idx;
    }
    static inline
    unsigned BSR64(uint64_t x) {
        bsr_idx_t idx;
        _BitScanReverse64(&idx, x); // ignore bool retval
        return idx;
    }
#Elif defined(__GNUC__)

  #ifdef __clang__
    static inline unsigned BSR64(uint64_t x) {
        return 63-__builtin_clzll(x);
      // gcc/ICC can't optimize this back to just BSR, but clang can and doesn't provide alternate intrinsics
    }
  #else
    #define BSR64 __builtin_ia32_bsrdi
  #endif

    #include <x86intrin.h>
    #define BSR32(x) _bit_scan_reverse(x)

#endif
_

bsfはおそらくコンパイラにとってそれほど助けを必要としません。なぜなら、ビルトインはLSBのビットインデックス、つまり後続ゼロのカウントを返すasm命令の動作と一致するからです。

テスト呼び出し元unsigned test32(unsigned x) { return BSR32(x); }は、すべての主要なx86コンパイラーで1命令にインライン化します Godboltコンパイラーエクスプローラー 。 BSR64は同じ方法で、64ビットのオペランドサイズバージョンにインライン化します。 最上位ビット以下のすべてのビットをゼロにするx86/x86_64命令はありますか? ユースケースなども参照してください。

_;; x64 MSVC 19.16 -O2
unsigned int test32(unsigned int) PROC                                    ; test32, COMDAT
        bsr     eax, ecx
        ret     0
unsigned int test32(unsigned int) ENDP                                    ; test32
_
_# clang -O3 -march=haswell   is too "smart?" for its own good:
test32(unsigned int):
        lzcnt   eax, edi
        xor     eax, 31
        ret
_
_# gcc8.2 -O3 -march=haswell
test32(unsigned int):
        bsr     eax, edi
        ret
_
_# ICC19 -O3 -march=haswell
test32(unsigned int):
        bsr       eax, edi                                      #15.9
        ret                                                     #41.12
_

これのポイントは、ポータブル(非MSVC)バージョンからの遅いコードを避けることです:

_#ifdef __GNUC__
unsigned badgcc(uint64_t x) {
    return 63 - __builtin_clzll(x);
}
#endif
_

_-march=haswell_がなければ、clangからBSRを取得しますが、次のようになります。

_# gcc8.2 -O3
badgcc(unsigned long):
        bsr     rdi, rdi
        mov     eax, 63
        xor     rdi, 63
        sub     eax, edi
        ret
_
_# ICC19.0.1 -O3
badgcc(unsigned long):
        mov       rax, -1                                       #46.17
        bsr       rdx, rdi                                      #46.17
        cmove     rdx, rax                                      #46.17
        neg       rdx                                           #46.17
        add       rdx, 63                                       #46.17
        neg       edx                                           #46.17
        add       edx, 63                                       #46.17
        mov       eax, edx                                      #46.17
        ret                                                     #46.17
_

それはただ厄介です。 (ICCがCMOVを実行して、入力がゼロの場合_-1_を生成することを確認するのは興味深い。BSRは、inputに従ってZFを設定します。結果に)

_-march=haswell_(またはBMI1命令の使用を有効にする)を使用すると、それほど悪くはありませんが、BSRほどではありません。モジュロ出力の依存関係。コンパイラは主にlzcntを回避するために動作しますが、奇妙なことにBSRを回避しません。 (input = 0の振る舞いのため、出力依存関係がtrue依存関係である場合) LZCNT問題の「出力依存関係」を破る理由

1
Peter Cordes

純粋なCでこれを行うための2つの最良の方法:

最初にバイト/ワード配列を線形検索してゼロでない最初のバイト/ワードを見つけてから、見つけたバイト/ワードの展開されたバイナリ検索を行います。

if (b>=0x10)
  if (b>=0x40)
    if (b>=0x80) return 0;
    else return 1;
  else
    if (b>=0x20) return 2;
    else return 3;
else
  if (b>=0x4)
    if (b>=0x8) return 4;
    else return 5;
  else
    if (b>=0x2) return 6;
    else return 7;

3(ちなみにlog2(8)です)答えを得るための条件付きジャンプ。最新のx86マシンでは、最後のマシンが条件付きmovに最適化されます。

または、ルックアップテーブルを使用して、設定されている最初のビットのインデックスにバイトをマッピングします。

関連するトピックは、整数log2関数です。思い出すと、ffmpegにはNice実装があります。

編集:実際には上記のバイナリ検索をブランチレスバイナリ検索にすることができますが、この場合の方が効率的かどうかはわかりません...

1
R..

__builtin_clz()を説明するコードスニペットを次に示します。

////// go.c ////////
#include <stdio.h>

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)


int main()
{
  unsigned ui;

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

  return 0;
}
1
George

タグは32ビットを示していますが、使用している値は16ビットのようです。あなたが32ビットを意味していた場合、0x00a1の答えは8ではなく24でなければなりません。

左側からMSBビットインデックスを探していて、uint32_tのみを処理することがわかっていると仮定した場合、ここに明確で単純なアルゴリズムがあります。

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

int main()
{
    uint32_t test_value = 0x00a1;
    int i;

    for (i=0; i<32; ++i)
    {
        if (test_value & (0x80000000 >> i))
        {
            printf("i = %d\n", i);
            exit(0);
        }
    }

    return 0;
}
0
tikiboy

Java私はこれを使用します:

static public final int msb(int n) {
    n |= n >>> 1;  
    n |= n >>> 2; 
    n |= n >>> 4; 
    n |= n >>> 8; 
    n |= n >>> 16; 
    n >>>= 1;
    n += 1; 
    return n;
}

そして:

static public final int msb_index(int n) {

    final int[] multiply_de_bruijn_bit_position = {
        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
    };
    return multiply_de_bruijn_bit_position[(msb(n) * 0x077CB531) >>> 27];
}
0
clankill3r

これは、任意のサイズのバイト配列に対する単純なブルートフォースアルゴリズムです。

_int msb( unsigned char x);  // prototype for function that returns 
                            //  most significant bit set

unsigned char* p;

for (p = arr + num_elements; p != arr;) {
    --p;
    if (*p != 0) break;
}

// p is with pointing to the last byte that has a bit set, or
//  it's pointing to the first byte in the array

if (*p) {
    return ((p - arr) * 8) + msb( *p);
}

// what do you want to return if no bits are set?
return -1;
_

適切なmsb()関数とintまたは_long long_サイズのデータ​​のちらつきを処理するための最適化を考え出す読者のための演習として残しておきます。

0
Michael Burr