web-dev-qa-db-ja.com

Cの整数で最高のセットビット(msb)を見つけるための最速/最も効率的な方法は何ですか?

整数nがあり、最上位ビットの位置を知りたい場合(つまり、最下位ビットが右側にある場合、1である最も左のビットの位置を知りたい場合)、見つけるための最も迅速/最も効率的な方法は何ですか?

POSIXは、最初の設定ビットを見つけるために、strings.hのffs()メソッドをサポートしていることを知っていますが、対応するfls()メソッドはないようです。

これを行うための本当に明らかな方法はありますか?

移植性のためにPOSIX関数を使用できない場合はどうですか?

編集:32ビットアーキテクチャと64ビットアーキテクチャの両方で動作するソリューションについてはどうでしょう(多くのコードリストは32ビットintでのみ動作するようです)。

110
Zxaos

GCCが持っている

 -組み込み関数:int __builtin_clz(unsigned int x)
最上位ビット
から始まるXの先頭の0ビットの数を返します。 Xが0の場合、結果は未定義です。
 
-組み込み関数:int __builtin_clzl(unsigned long)
引数タイプが `以外は` __builtin_clz 'と同様unsigned 
 long '。
 
-組み込み関数:int __builtin_clzll(unsigned long long)
 `__builtin_clz'と似ていますが、引数の型が` unsigned 
 long long '。

それらは、それらの派手なビット調整アルゴリズムの1つであっても、単一の命令であっても、現在のプラットフォームにとって合理的に効率的なものに変換されると期待しています。


入力がゼロである場合の便利なトリックは__builtin_clz(x | 1):他を変更せずに無条件で下位ビットを設定すると、0の出力がx=0になります。他の入力の出力を変更することなく。

それを行う必要を避けるために、他のオプションは、ARM GCCの__clz(ヘッダーは不要)、またはlzcnt命令をサポートするCPU上のx86の_lzcnt_u32などのプラットフォーム固有の組み込み関数です。 (フォールトするのではなく、古いCPUではlzcntbsrとしてデコードすることに注意してください。これにより、ゼロ以外の入力に対して31-lzcntが生成されます。)

残念ながら、input = 0の結果を32または64(オペランドの幅に応じて)として定義する非x86プラットフォームでさまざまなCLZ命令を移植可能に利用する方法はありません。 x86のlzcntもそれを行いますが、bsrは、31-__builtin_clz(x)を使用しない限りコンパイラが反転する必要のあるビットインデックスを生成します。

(「未定義の結果」はCの未定義の動作ではなく、単に定義されていない値です。実際に命令が実行されたときの宛先レジスタにあったものです。AMDしかし、それはnot以前に割り当てていたC変数にあったものであり、通常、gccがCをasmに変換するときの動作ではありません。 なぜLZCNT問題の「出力依存性」を破るのですか?

58
ephemient

X86とゲームを少しインラインアセンブラで使用していると仮定すると、Intelは BSR 命令(「ビットスキャンリバース」)を提供します。 fast onsomex86(他ではマイクロコード化)。マニュアルから:

ソースオペランドで最上位セットビット(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)。

40
timday

2 ^ NはN番目のビットセット(1 << N)のみを持つ整数であるため、最上位セットビットの位置(N)を見つけることは、その整数の整数対数2です。

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

unsigned int v;
unsigned r = 0;

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

この「明らかな」アルゴリズムは誰にも透過的ではないかもしれませんが、左端のビットがシフトされるまでコードが繰り返し1ビットずつ右にシフトすることに気付いたとき(Cはゼロ以外の値をtrueとして扱うことに注意してください)シフトの、それは完全に理にかなっています。また、複数のビットが設定されている場合でも機能することを意味します。結果は常に最上位ビットになります。

そのページを下にスクロールすると、より高速で複雑なバリエーションがあります。ただし、多数の先行ゼロを含む数値を処理していることがわかっている場合、Cではビットシフトがかなり高速であり、単純なアルゴリズムでは配列のインデックス付けが必要ないため、単純なアプローチでは許容可能な速度が得られます。

注:64ビット値を使用する場合は、非常に巧妙なアルゴリズムを使用する場合は十分に注意してください。それらの多くは32ビット値に対してのみ正しく機能します。

36
Quinn Taylor

これは非常に高速です。

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];
}
17
Protagonist

これは一種の整数ログを見つけるようなものです。ちょっとした工夫がありますが、私はこのための独自のツールを作成しました。もちろん、目標はスピードです。

私の認識では、CPUには既に整数から浮動小数点への変換に使用される自動ビット検出器があります!それを使用します。

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

このバージョンでは、値をdoubleにキャストしてから指数を読み取り、ビットがどこにあったかを示します。派手なシフトと減算は、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以降では、union {double d; uint32_t u[2];};を使用します。ただし、C++では、union型のパンニングは一部のコンパイラでのみ拡張としてサポートされており、ISO C++ではサポートされていません。


これは通常、先行ゼロカウント命令のプラットフォーム固有の組み込み関数よりも遅くなりますが、移植可能なISO Cにはそのような機能はありません。一部のCPUには先行ゼロカウント命令もありませんが、それらのいくつかは整数をdoubleに効率的に変換できます。 FPビットパターンを整数に戻すタイプパニングは遅い場合があります(たとえば、PowerPCではストア/リロードが必要で、通常はロードヒットストアのストールが発生します)。

このアルゴリズムは、SIMD lzcntを持つCPUが少ないため、SIMDの実装に役立つ可能性があります。 x86はそのような命令のみを取得しました with AVX512CD

13
SPWorley

カズ・キルヘクはこちら

このために、63ビットの数値(gcc x86_64のlong long型)に対して2つのアプローチのベンチマークを行い、符号ビットを避けました。

(何かのために、この「最高位ビットを見つける」必要が生じることがあります。)

データ駆動型のバイナリ検索を実装しました(上記の回答の1つに密接に基づいています)。また、完全に展開された決定ツリーを手動で実装しました。これは、即値オペランドを持つコードです。ループもテーブルもありません。

決定木(highest_bit_unrolled)は、バイナリ検索で明示的なテストが行​​われるn = 0の場合を除いて、69%高速になるようベンチマークされました。

バイナリ検索の0ケースの特別なテストは、特別なテストを持たない決定木よりも48%だけ高速です。

コンパイラ、マシン:(GCC 4.5.2、-O3、x86-64、2867 Mhz Intel Core 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のみを使用すると、差は大きくなります。決定木はほぼ4倍高速です。

また、素朴なビットシフトコードに対するベンチマークも行いました。

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

予想されるように、これは小さな数値に対してのみ高速です。 n == 1の場合、最上位ビットが1であると判断した結果、80%以上高速にベンチマークが行われました。ただし、63ビット空間でランダムに選択された数値の半分には、63番目のビットが設定されています!

入力0x3FFFFFFFFFFFFFFFでは、決定木バージョンは1よりもかなり高速であり、ビットシフターよりも1120%(12.2倍)高速であることが示されています。

また、GCCビルトインに対する決定ツリーのベンチマークを行い、同じ番号に対して繰り返すのではなく、入力の混合を試みます。いくつかのスティック分岐予測が行われている可能性があり、おそらく非現実的なキャッシングシナリオがあり、それにより反復で人為的に高速化されています。

10
Kaz

どう?

int highest_bit(unsigned int a) {
    int count;
    std::frexp(a, &count);
    return count - 1;
}

7
Marco Amagliani

おそらく最高のパフォーマンスを絶対に必要とする場合にのみこの方法を使用しますが(ビットボードを含む何らかのボードゲームAIを作成する場合など)、最も効率的なソリューションはインラインASMを使用することです。説明付きのコードについては、 このブログ投稿 の最適化セクションを参照してください。

[...]、bsrlアセンブリ命令は、最上位ビットの位置を計算します。したがって、次のasmステートメントを使用できます。

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

以下は、このページで現在提供されているアルゴリズムの(単純な)ベンチマークです。

アルゴリズムは、unsigned 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;
}
5
Josh
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命令。信じられないかもしれませんが、これは通常、線形時間で動作する上記のBSR命令よりも高速です。これは対数時間です。

から http://aggregate.org/MAGIC/#Most%20Significant%201%20Bit

5
rlbond

それはある種のバイナリ検索で、あらゆる種類の(符号なし!)整数型で動作します

#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

私はこれを行うルーチンが必要であり、ウェブを検索する(そしてこのページを見つける)前に、バイナリ検索に基づく独自のソリューションを思いつきました。誰かがこれをやったことがあると確信していますが!これは一定の時間で実行され、投稿された「明白な」ソリューションよりも高速になる可能性がありますが、私は大きな主張をするのではなく、興味のあることを投稿するだけです。

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
dangermouse

ここでいくつかの過度に複雑な答え。 Debruinテクニックは、入力が既に2のべき乗である場合にのみ使用する必要があります。そうでない場合は、より良い方法があります。 2のべき乗の入力の場合、Debruinは、私がテストしたすべてのプロセッサで_BitScanReverseよりもさらに速い、絶対的な最速です。ただし、一般的な場合、_BitScanReverse(またはコンパイラーで呼び出される組み込み関数)は最速です(特定のCPUではマイクロコード化できます)。

組み込み関数がオプションではない場合、一般的な入力を処理するための最適なソフトウェアソリューションがあります。

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;
}

このバージョンでは、他のほとんどの回答とは異なり、最後にDebruinルックアップを必要としないことに注意してください。所定の位置を計算します。

ただし、テーブルの方が望ましい場合がありますが、テーブルを十分に繰り返し呼び出すと、テーブルの高速化によってキャッシュミスのリスクが隠されます。

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;
}

これは、ここで与えられたソフトウェアの答えの中で最高のスループットを生成するはずですが、たまにしか呼び出さない場合は、最初のスニペットのようなテーブルのないソリューションをお勧めします。

4
VoidStar

上記の答えが指摘しているように、最上位ビットを決定する方法はいくつかあります。ただし、これも指摘されているように、メソッドは32ビットまたは64ビットのレジスタに固有のものである可能性があります。 stanford.edu bithacks page は、32ビットと64ビットの両方のコンピューティングで機能するソリューションを提供します。少しの作業で、これらを組み合わせて、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;
}
3
David C. Rankin

逐次近似を使用した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);
}

利点:ループの数は常に同じであるため、実行時間は指定された数に関係なく一定です。 (「unsigned int」を使用すると4ループ)

3
user3177100

c99 が与えてくれた log2 。これにより、このページに表示される特別なソースlog2の実装がすべて不要になります。次のように、標準のlog2実装を使用できます。

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

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

0ULnも同様に保護する必要があります。

-∞が返され、FE_DIVBYZEROが発生します

ここでIndexULONG_MAXに任意に設定するチェックを使用した例を作成しました: https://ideone.com/u26vsi


visual-studioephemient's gcc only answer の帰結は:

const auto n = 13UL;
unsigned long Index;

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

_BitScanReverseのドキュメント は、Indexが次のとおりであると述べています:

最初に設定されたビット(1)のビット位置がロードされました

実際には、n0ULである場合、 Index0UL に設定されていることがわかりました。これは、1ULnと同じです。しかし、0ULnの場合にドキュメントで保証される唯一のことは、戻り値が次のとおりであることです。

設定ビットが見つからなかった場合は0

したがって、上記の望ましいlog2実装と同様に、この場合、Indexにフラグ付きの値を設定して、戻り値を確認する必要があります。ここで、このフラグ値にULONG_MAXを使用する例を再度記述しました。 http://rextester.com/GCU61409

3
Jonathan Mee

私はこの質問が非常に古いことを知っていますが、 msb()関数を自分で実装しただけで、ここおよび他のWebサイトで提示されるほとんどのソリューションは必ずしも最も効率的ではないことがわかりました-少なくとも私の効率の定義については(下記の Update も参照)。その理由は次のとおりです。

ほとんどのソリューション(特に、ある種のバイナリ検索スキームまたは右から左への線形スキャンを行う単純なアプローチを採用しているソリューション)は、任意のバイナリ番号に対して、非常に長いシーケンスで始まるものは多くないという事実を無視しているようですゼロ。実際、ビット幅については、すべての整数の半分が1で始まり、4分の1が1で始まります。私がどこに着いているのか見ますか?私の主張は、リニアスキャンが最上位ビット位置から最下位(左から右)まで、一見したように「線形」ではないということです。

見せられる1、どのビット幅でも、テストする必要がある平均ビット数は最大2です。これは、 amortized 時間の複雑さ O(1)ビット数(!)に関して。

もちろん、最悪の場合はまだ O(n)であり、 O(log(n) )バイナリ検索のようなアプローチで得られますが、最悪のケースはほとんどないため、ほとんどのアプリケーションでは無視できます( Update :まったくない:少ないかもしれませんが、高い確率で発生する可能性があります- Update 下記を参照)。

ここに私が思いついた「ナイーブな」アプローチがあります。少なくとも私のマシンでは、他のほとんどのアプローチに勝っています(32ビットintのバイナリ検索スキームでは、常に log 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;
}

Update :ここで書いたことは、 arbitrary integersには完全に当​​てはまります。おそらく(私の速度テストは、 all 32ビット整数のMSBを決定するのにかかった時間を測定しただけです)、そのような関数が呼び出される実際の整数です。別のパターン:たとえば、私のコードでは、この関数を使用して、オブジェクトサイズが2のべき乗かどうかを判断したり、次の2のべき乗以上を見つけたりします。 オブジェクトサイズ。私の推測では、MSBを使用するほとんどのアプリケーションは、整数が表すことができる最大数よりもはるかに小さい数を含んでいると考えられます(オブジェクトサイズは size_t この場合、私のソリューションは実際にはバイナリ検索アプローチよりもパフォーマンスが低下します。したがって、 all 整数のループ処理は高速になりますが、後者の方がおそらく好ましいでしょう。
TL; DR:実際の整数は、おそらくこの単純なアルゴリズムの最悪のケースに偏っており、最終的にパフォーマンスが悪化しますが、 amortized O(1)本当に任意の整数の場合。

1引数は次のようになります(大まかなドラフト): n をビット数(ビット幅)とします。合計があります 2n  n ビットで表現できる整数。がある 2n-1 1で始まる整数(最初の1は固定、残り n-1 ビットは何でも構いません)。これらの整数は、MSBを決定するためにループの1回の反復のみを必要とします。さらに、 2n-2 1で始まる整数、2回の反復が必要、 2n-3 1で始まる整数、3回の反復が必要など。

すべての可能な整数に必要なすべての反復を合計し、それらを除算すると 2n、整数の合計数、 n -bit integersのMSBを決定するために必要な反復の平均数を取得します。

(1 * 2n-1 + 2 * 2n-2 + 3 * 2n-3 + ... + n)/ 2n

この一連の平均反復は実際には収束的であり、 n 無限に向かって2の制限があります

したがって、単純な左から右のアルゴリズムには、実際には amortized の一定時間の複雑さがあります O(1) =任意のビット数。

2
Finnegan

ビットごとの演算子を考えてください。

この質問を初めて誤解しました。左端のビットが設定されたintを生成する必要があります(他のビットはゼロ)。 cmpがその値に設定されていると仮定します:

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

Joshのベンチマークを拡張... clzを次のように改善できます。

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

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

Asmについて:bsrとbsrlがあることに注意してください(これは「長い」バージョンです)。通常の方が少し速いかもしれません。

2
JonesD

「まだ別の」アプローチであるため、これを入れることは、すでに与えられている他のアプローチとは異なるようです。

-1の場合はx==0を返し、そうでない場合はfloor( log2(x))(最大結果31)

32ビットから4ビットの問題を減らしてから、テーブルを使用します。おそらくエレガントではありませんが、実用的です。

これは、移植性の問題のため__builtin_clzを使用したくないときに使用するものです。

コンパクトにするために、代わりにループを使用して削減し、毎回4をrに追加して、最大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
greggo

私の謙虚な方法は非常に簡単です:

MSB(x)= INT [Log(x)/ Log(2)]

変換:xのMSBは、(ベースxのログをベース2のログで割った)の整数値です。

これは、あらゆるプログラミング言語に簡単かつ迅速に適応できます。計算機で試してみて、動作することを確認してください。

1
SpartanWar

うわー、それは多くの答えでした。古い質問に答えて申し訳ありません。

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
Harry Svensson

あなたがしようとしていることは、整数の整数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));
}

一度に複数のビットを検索できることを確認してください。

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
ChuckCottrill

あなたの質問は整数(以下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 & 0x8000000) && 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に変更)。何か忘れてしまったら教えてください。私はそれをテストしていませんが、うまく動作するはずです。

0
Antonin GAVREL

別のポスターは、byte-wideルックアップを使用してlookup-tableを提供しました。 (256個のルックアップエントリではなく、32Kのメモリのコストで)もう少しパフォーマンスを高めたい場合は、15ビットルックアップテーブルを使用したソリューションがありますC#7for。NET

興味深い部分は、テーブルの初期化です。プロセスの存続期間中に必要な比較的小さなブロックなので、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;
}

このテーブルでは、上記のコードによる1回限りの初期化が必要です。読み取り専用なので、単一のグローバルコピーを共有して同時アクセスできます。このテーブルを使用すると、整数logをすばやく検索できます2これは、さまざまな整数幅(8、16、32、および64ビット)について、ここで探しているものです。

「最高設定ビット」の概念が未定義である唯一の整数である0のテーブルエントリには、値-1が与えられていることに注意してください。この区別は、以下のコードで0の値を持つ上位ワードを適切に処理するために必要です。これ以上苦労することなく、さまざまな整数プリミティブのそれぞれのコードを次に示します。

ulong(64-bit)Version

/// <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としてコード化されたバージョンは、多くの実行で一貫した勝者でした。アクティブな作業/スクラッチ形式のこれらのさまざまな候補は、 herehere 、および here にあります。

 1人の候補者。HighestOne_Tab16A622,496 
 2人の候補者。HighestOne_Tab16C628,234 
 3人の候補者。HighestOne_Tab8A649,146 
 4人の候補者。HighestOne_Tab8B656,847 
 5人の候補者。HighestOne_Tab16B657。 6人の候補者。HighestOne_Tab16D659,650 
 7 _highest_one_bit_UNMANAGED.HighestOne_U 702,900 
 8 de_Bruijn.IndexOfMSB 709,672 
 9 _old_2.HighestOne_Old2 715,810 _.____。 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 [.____ _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_NoTab777,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.HighestOne 824,345 
 30 _bitarray_ext.RtlFindMostSignificantBit 894,069 
 31候補。HighestOne_Naive898,865

注目すべきは、P/Invokeを介したntdll.dll!RtlFindMostSignificantBitのひどいパフォーマンスです。

[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

これらの5行に起因するパフォーマンスの低下を想像することはできません。そのため、管理されたネイティブの移行ペナルティが原因であるに違いありません。また、テストで128バイト(および256バイト)short(8ビット)よりも32 KB(および64 KB)byte(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];
}

最後に指摘するのは、私のdeBruijnメソッドがうまくいかなかったことにかなりショックを受けたことです。これは私が以前広く使用していた方法です:

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];
}

DeBruijnのメソッドがどれほど優れていて素晴らしいかについて多くの議論があります このSOの質問で 、そして私は同意する傾向がありました。私の推測では、deBruijnと直接ルックアップテーブルメソッドの両方(私が最速であることがわかった)は両方ともテーブルルックアップを行う必要があり、両方とも非常に最小限の分岐を持っていますが、deBruijnのみが64ビットの乗算演算を持っています。ここではIndexOfMSB関数のみをテストしましたが、deBruijn IndexOfLSBはテストしていませんが、操作が非常に少ないため(上記参照)、後者の方がはるかに良いチャンスを期待しています。 LSBで引き続き使用します。

0
Glenn Slayden

コード:

    // 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)の整数部を取得します

0
jemin