J'ai une implémentation de tableau de bits où le 0ème index est le MSB du premier octet dans un tableau, le 8ème index est le MSB du deuxième octet, etc ...
Comment trouver rapidement le premier bit défini dans ce tableau de bits? Toutes les solutions connexes que j'ai recherchées trouvent le premier bit le moins significatif, mais j'ai besoin du premier le plus significatif. Donc, étant donné 0x00A1, je veux 8 (car c'est le 9e bit de gauche).
GCC a __builtin_clz
qui se traduit par BSR sur x86/x64, CLZ sur ARM, etc. et émule l'instruction si le matériel ne l'implémente pas.
Visual C++ 2005 et plus a _BitScanReverse
.
tl: dr; Pour 32 bits, utilisez multiplication de Bruijn .
C'est l'algorithme portable "" le plus rapide " . Il est sensiblement plus rapide et plus correct que tous les autres algorithmes MSB 32 bits portables de ce thread.
L'algorithme de Bruijn renvoie également un résultat correct lorsque l'entrée est nulle. Les instructions __builtin_clz et _BitScanReverse retournent des résultats incorrects lorsque l'entrée est nulle.
Sous Windows x86-64, la multiplication de Bruijn s'exécute à une vitesse comparable à la fonction équivalente (imparfaite) de Windows, avec une différence de performances d'environ 3% seulement.
Voici le code.
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];
}
Toutes les autres réponses de ce fil fonctionnent soit beaucoup plus mal que leurs auteurs ne le suggèrent, ou ne calculent pas le résultat correctement, ou les deux. Essayons de les comparer tous et vérifions qu'ils font ce qu'ils prétendent faire.
Voici un simple faisceau C++ 11 pour tester toutes ces implémentations. Il compile proprement sur Visual Studio mais devrait fonctionner sur tous les compilateurs modernes. Il vous permet d'exécuter le test en mode performance (bVerifyResults = false) et en mode vérification (bVerifyResults = true).
Voici les résultats en mode vérification:
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
Le "junkie de performance" et les implémentations natives de Microsoft font des choses différentes lorsque l'entrée est nulle. msbPerformanceJunkie32 produit -1 et _BitScanReverse de Microsoft produit un nombre aléatoire, cohérent avec l'instruction matérielle sous-jacente. L'implémentation msbPerformanceJunkie32 produit également un résultat qui est désactivé par l'une de toutes les autres réponses.
Voici les résultats en mode performance, fonctionnant sur mon ordinateur portable i7-4600, compilés en mode release:
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
La version de Bruijn bat les autres implémentations sainement car elle est sans branche, et donc elle fonctionne bien avec des entrées qui produisent un ensemble de sorties uniformément réparties. Toutes les autres versions sont plus lentes contre les entrées arbitraires en raison des pénalités de mauvaise prédiction de branche sur les processeurs modernes. La fonction smbFfs produit des résultats incorrects et peut donc être ignorée.
Certaines implémentations fonctionnent sur des entrées 32 bits et d'autres sur des entrées 64 bits. Un modèle nous aidera à comparer les pommes aux pommes, quelle que soit la taille d'entrée.
Voici le code. Téléchargez et exécutez les benchmarks vous-même si vous le souhaitez.
#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
}
En tant que junkie de la performance, j'ai essayé une tonne de variations pour l'ensemble MSB, ce qui suit est le plus rapide que j'ai rencontré,
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];
}
Il existe plusieurs façons de le faire, et les performances relatives des différentes implémentations dépendent quelque peu de la machine (il se trouve que je l'ai comparé dans une certaine mesure pour un objectif similaire). Sur certaines machines, il y a même une instruction intégrée pour cela (utilisez-en une si disponible et la portabilité peut être traitée).
Découvrez quelques implémentations ici (sous "base de journaux entiers 2"). Si vous utilisez GCC, consultez les fonctions __builtin_clz
et __builtin_clzl
(qui le font respectivement pour les entiers non signés et les longs non signés différents de zéro). Le "clz" signifie "compter les zéros non significatifs", qui est encore une autre façon de décrire le même problème.
Bien sûr, si votre tableau de bits ne tient pas dans un mot de machine approprié, vous devez parcourir les mots du tableau pour trouver le premier mot non nul, puis effectuer ce calcul uniquement sur ce mot.
Recherchez l'instruction asm BSR (Bit scan reverse) x86 pour le moyen le plus rapide de le faire. Du doc d'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).
Si vous utilisez x86, vous pouvez battre pratiquement n'importe quelle solution octet par octet ou mot par mot en utilisant les opérations SSE2, combinées aux instructions find-first-bit, qui (dans le monde gcc) sont prononcées "ffs "pour le bit le plus bas et" fls "pour le bit le plus élevé. Pardonnez-moi d'avoir des problèmes (! @ # $% ^) À formater le code "C" dans une réponse; consultez: http://mischasan.wordpress.com/2011/11/03/sse2-bit-trick-ffsfls-for-xmm-registers/
J'ai travaillé avec un certain nombre de fonctions pour obtenir le bit le plus significatif, mais des problèmes surviennent généralement entre les nombres 32 et 64 bits ou entre les cases x86_64 et x86. Les fonctions __builtin_clz
, __builtin_clzl
et __builtin_clzll
fonctionne bien pour les nombres 32/64 bits et sur les machines x86_64 et x86. Cependant, trois fonctions sont requises. J'ai trouvé un MSB simple qui s'appuie sur le décalage à droite qui gérera tous les cas pour les nombres positifs. Au moins pour l'usage que j'en fais, il a réussi là où d'autres ont échoué:
int
getmsb (unsigned long long x)
{
int r = 0;
if (x < 1) return 0;
while (x >>= 1) r++;
return r;
}
En désignant l'entrée comme unsigned long long
il peut gérer toutes les classes de nombres de unsigned char
à unsigned long long
et étant donné la définition standard, il est compatible avec les versions x86_64 et x86. Le cas pour 0
est défini pour renvoyer 0
, mais peut être modifié selon les besoins. Un test et une sortie simples sont:
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;
}
Production:
0 MSB : 0
216 MSB : 7
1021 MSB : 9
32767 MSB : 14
32768 MSB : 15
3297381253 MSB : 31
323543844043 MSB : 38
REMARQUE: pour des considérations de vitesse, utiliser une seule fonction pour accomplir la même chose centrée autour de __builtin_clzll
est encore plus rapide d'un facteur d'environ 6.
J'en ajouterai un!
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;
}
Bien sûr, cela fonctionne sur un nombre 64 bits (non signé long long), et non sur un tableau. En outre, de nombreuses personnes ont signalé des fonctions g ++ intégrées que je ne connaissais pas. Comme c'est intéressant.
Quoi qu'il en soit, cela trouve le bit le plus significatif en 6 itérations et donne une affirmation si vous avez passé 0 à la fonction. Pas la meilleure fonction à utiliser si vous avez accès à une instruction du chipset.
J'utilise aussi | = au lieu de + = parce que ce sont toujours des puissances de deux, et OR est (classiquement) plus rapide que l'addition. Puisque j'ajoute seulement des puissances uniques de 2 ensemble, Je ne me suis jamais retourné.
Il s'agit d'une recherche binaire ce qui signifie qu'elle trouve toujours le résultat en 6 itérations.
Encore une fois, c'est mieux:
u8 findMostSignificantBit2 (u64 u64Val)
{
assert (u64Val != 0ULL);
return (u8) (__builtin_ctzll(u64Val));
}
Pas le plus rapide, mais ça marche ...
//// 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;
}
x86 a une instruction BSR qui retourne un index binaire (plutôt que le nombre de zéros en tête ci-dessus il).
Mais malheureusement, il n'y a pas intrinsèque portable qui efficacement l'expose pour tous les compilateurs. GNU C fournit __builtin_clz
, Mais unsigned bitidx = 31 - __builtin_clz(x);
n'optimise pas uniquement BSR avec GCC et ICC actuels (il le fait avec clang, ce qui prouve que l'expression est équivalente donc pourrait).
Ce qui suit définit les macros ou les fonctions BSR32()
et BSR64()
qui se compilent efficacement pour juste une instruction bsr
sur x86. (Produire un résultat incorrect si l'entrée était nulle. Il n'y a aucun moyen avec les intrinsèques de tirer parti du comportement de l'instruction asm de laisser la destination non modifiée pour l'entrée = 0.)
La portabilité vers des non-x86 nécessiterait un supplément de #ifdef
par exemple pour revenir à 31-__builtin_clz
. La plupart des ISA non x86, si elles ont un bitcan de début zéro, comptent les zéros de tête au lieu de vous donner l'index de bit. C'est pourquoi GNU C définit __builtin_clz
Comme le module intégré portable. (S'il n'y a pas de prise en charge matérielle sur le système cible, le module intégré se compilera en émulation logicielle, appelant généralement une fonction d'aide 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
n'a probablement pas besoin d'autant d'aide pour les compilateurs, car le code intégré correspond au comportement de l'instruction asm consistant à renvoyer l'indice binaire du LSB, c'est-à-dire le nombre de zéros à la fin.
Un appelant de test unsigned test32(unsigned x) { return BSR32(x); }
l'inline à 1 instruction sur tous les principaux compilateurs x86, sur l'explorateur du compilateur Godbolt . BSR64 s'aligne de la même manière, à une version de taille d'opérande 64 bits. Voir aussi Existe-t-il une instruction x86/x86_64 qui met à zéro tous les bits en dessous du bit le plus significatif? pour des exemples d'utilisation.
;; 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
Le but de ceci est d'éviter le code lent de la version portable (vers non-MSVC):
#ifdef __GNUC__
unsigned badgcc(uint64_t x) {
return 63 - __builtin_clzll(x);
}
#endif
Sans -march=haswell
Nous obtenons juste BSR de clang, mais:
# 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
C'est juste méchant. (Il est intéressant de voir que ICC fait un CMOV pour produire -1
Si l'entrée est nulle. BSR définit ZF en fonction de son entrée, contrairement à la plupart des instructions qui définissent des indicateurs en fonction du résultat. )
Avec -march=haswell
(Ou autrement activer l'utilisation des instructions BMI1), ce n'est pas aussi mauvais, mais toujours pas aussi bon que juste BSR. Dépendances de sortie Modulo, que les compilateurs travaillent principalement à éviter pour lzcnt mais étrangement pas pour BSR. (Lorsque la dépendance de sortie est une dépendance true, en raison du comportement d'entrée = 0.) Pourquoi la rupture de la "dépendance de sortie" de LZCNT importe-t-elle?
Deux meilleures façons de le faire en C pur:
Effectuez d'abord une recherche linéaire dans le tableau d'octets/mots pour trouver le premier octet/mot différent de zéro, puis effectuez une recherche binaire non déroulée de l'octet/mot que vous trouvez.
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 (BTW c'est log2 (8)) sauts conditionnels pour obtenir la réponse. Sur les machines x86 modernes, la dernière sera optimisée en un mouvement conditionnel.
Vous pouvez également utiliser une table de recherche pour mapper l'octet à l'index du premier bit défini.
Une rubrique connexe que vous voudrez peut-être rechercher est les fonctions log2 entières. Si je me souviens bien, ffmpeg a une implémentation de Nice.
Edit: Vous pouvez réellement transformer la recherche binaire ci-dessus en une recherche binaire sans branche, mais je ne sais pas si ce serait plus efficace dans ce cas ...
Voici un extrait de code expliquant __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;
}
Um, votre balise indique 32 bits mais il semble que les valeurs que vous utilisez sont de 16 bits. Si vous vouliez dire 32 bits, je pense que la réponse pour 0x00a1 devrait être 24 et non 8.
En supposant que vous recherchez l'index de bits MSB du côté gauche et que vous savez que vous ne traiterez qu'avec des uint32_t, voici l'algorithme évident et simple:
#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;
}
Pour Java j'utilise ceci:
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;
}
Et:
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];
}
Voici un algorithme de force brute simple pour un tableau d'octets de taille arbitraire:
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;
Je vais laisser comme un exercice pour le lecteur de trouver une fonction msb()
appropriée ainsi que l'optimisation pour travailler sur int
ou long long
Les données.