Trouver bit de poids fort (le plus à gauche) qui est définie dans un tableau de bits
J'ai un tableau de bits de mise en œuvre où l'0e indice est le bit de poids fort du premier octet dans un tableau, la 8e édition de l'indice de l'ESM de la deuxième octet, etc...
Ce qui est un moyen rapide pour trouver le premier bit est définie dans ce tableau de bits? Toutes les solutions que j'ai regardé jusqu'à trouver le premier bit le moins significatif, mais j'ai besoin de le premier plus importante. Donc, étant donné 0x00A1, je veux 8 (puisque c'est le 9ème bit en partant de la gauche).
- N'est-ce pas le bit 7 le bit le plus significatif dans 0x00a1 (en supposant que le lsb est le bit 0)?
- Est votre tableau de bits de longueur arbitraire, ou s'inscrit-il dans une machine à mot?
- J'ai été en comptant à partir de la gauche. En binaire, j'obtiens un "0000/0000/1010/0001", c'est le 9ème bit, avec un indice de 8. j'ai fait une erreur, bien que, il devrait être de 8, pas 9.
- De l'interface, vous avez à votre tableau de bits? Quelles sont les opérations que vous pouvez effectuer sur elle?
- c'est un C tableau de caractères
- Il y a une autre page avec des détails déjà... stackoverflow.com/questions/671815/...
- Trouver le plus significatif bit est équivalent à l'entier logarithme binaire
Vous devez vous connecter pour publier un commentaire.
GCC a
__builtin_clozapine
qui se traduit par BSR sur x86/x64, la CLOZAPINE sur les BRAS, etc. et émule l'instruction si le matériel ne prend pas la mettre en œuvre.Visual C++ 2005 et jusqu'a
_BitScanReverse
.tl:dr; Pour 32 bits, utilisez de Bruijn multiplication.
C'est le "le plus rapide" portable algorithme. Il est beaucoup plus rapide et plus correcte que toutes les autres portable 32 bits MSB algorithmes dans ce fil.
La de Bruijn algorithme renvoie également à un résultat correct lorsque l'entrée est égale à zéro. L' __builtin_clozapine et _BitScanReverse instructions retourner des résultats incorrects lorsque l'entrée est égale à zéro.
Sur Windows x86-64, de Bruijn multiplication s'exécute à une vitesse comparable à l'équivalent (imparfait) fonction de Windows, avec une différence de performance de seulement 3%.
Voici le code.
Toutes les autres réponses dans ce thread, soit courir beaucoup plus de mal que leurs auteurs suggèrent, ou de ne pas calculer correctement les résultats, ou les deux. Nous allons comparer tous et de vérifier qu'ils font ce qu'ils prétendent faire.
Ici est un simple C++11 harnais pour tester toutes ces implémentations. Il compile propre sur Visual Studio, mais devrait fonctionner sur tous les compilateurs modernes. Il vous permet d'exécuter le test en mode de performance (bVerifyResults = false) et dans le mode de vérification (bVerifyResults = true).
Voici les résultats en mode de vérification:
La performance "junkie" et Microsoft native implémentations de faire des choses différentes lorsque l'entrée est égale à zéro. msbPerformanceJunkie32 produit de -1, et Microsoft _BitScanReverse produit un nombre aléatoire, compatible avec le matériel sous-jacent de l'instruction. Aussi la msbPerformanceJunkie32 mise en œuvre produit un résultat qui est désactivée par celui de toutes les autres réponses.
Voici les résultats en mode performance, en cours d'exécution sur mon i7-4600 portables, compilé en mode release:
La de Bruijn version beats les autres implémentations profondément parce qu'il est dépourvu de branches, et par conséquent, il fonctionne bien contre les entrées qui produisent une répartie uniformément ensemble de sorties. Toutes les autres versions sont plus lents contre l'arbitraire des intrants en raison des pénalités de la branche, les erreurs de prédiction sur les Processeurs modernes. Le smbFfs fonction produit des résultats incorrects de sorte qu'il peut être ignoré.
Certaines implémentations de travail sur 32 bits entrées, et un peu de travail sur 64 bits entrées. Un modèle va nous aider à comparer des pommes avec des pommes, quelle que soit la taille de saisie.
Voici le code. Téléchargez et exécutez les points de repère vous-même si vous le souhaitez.
msbLoop32
dans son calendrier, le sens qu'il apparaît deux fois plus lent qu'il ne l'est réellement.v
qui est moins une puissance de 2. Le PDF liées à seulement explique pourquoi la multiplication correspond à un changement quand il est une puissance de 2, donc j'aurais pensé à l'ajout de 1 serait nécessaire.test()
instanciation pourmsbLoop32
(et aussi pourmsbLoop64
, je remarque maintenant) sont appelés chacun avecbIsReference
ensemble detrue
, etTimer t
est défini avant cette étape d'initialisation (qui commence avecif (bIsReference)
, de sorte qu'il comprend ce initialisation dans la durée des mesures.bsr
oulzcnt
de l'enseignement? Ce qui se passe est que, tant que la routine éprouvée est "assez vite" l'indice de référence seulement des tests de la performance d'un réseau dense de boucle indirects/appel de la branche. Si vous corrigez l'indice de référence, vous trouverez que le natif solutions sont, dans leur forme brute, de 3 à 10 fois plus rapide que la deBruijn solutions.bsr
etbsf
instructions, qui ont un résultat indéfini de zéro, mais des Processeurs récents onttzcnt
etlzcnt
instructions de résoudre ce (bien que pourlzcnt
le sens de la réponse est inversé de sorte qu'il n'est pas un simple remplacement). Il peut être gênant pour l'obtenir, le compilateur de les émettre, cependant.Comme une performance junkie j'ai essayé des tonnes de variations pour le MSB ensemble, ce qui suit est le plus rapide que j'ai rencontré,
Il y a plusieurs façons de le faire, et de la performance relative des différentes implémentations est un peu dépendant de la machine (il m'arrive d'avoir comparé cela à une certaine mesure, pour un but similaire). Sur certaines machines, il y a même un construit-dans l'instruction de ce type (utilisez une si disponible et la portabilité peut être traitée).
Vérifier certaines implémentations ici (sous “entier logarithme de base 2”). Si vous utilisez GCC, découvrez les fonctions
__builtin_clz
et__builtin_clzl
(qui ne ce pour les non-zéro entiers non signés et non signés longs, respectivement). Le “clozapine” 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 rentre pas dans une machine adaptée mot, vous avez besoin pour itérer sur les mots dans le tableau pour trouver le premier non nul mot et ensuite effectuer ce calcul uniquement sur ce mot.
__builtin_clz
et__builtin_clzl
sont pas définies pour les 0 entrées (comme sauvegardés par la GCC documentation).Chercher le BSR (Bit de balayage inverse) asm x86 instruction pour le moyen le plus rapide pour ce faire. À partir d'Intel doc:
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).
http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
#IntegerLogObvious
inclus, dans ma réponse.Si vous utilisez x86, vous pouvez battre pratiquement n'importe quel octet-par-octet ou mot-à-mot la solution à l'aide de la SSE2 opérations, combiné avec la première bits instructions, qui, dans le gcc monde) sont prononcées "ffs" pour le bit de poids faible et "fls" pour le bit le plus élevé.
Pardonnez-moi d'avoir mal (!@#$%^) mise en forme "C" code de réponse; découvrez:
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 se posent généralement se déplacer entre 32 bits et 64 bits des nombres ou en se déplaçant entre x86_64 et x86 boîtes. Les fonctions
__builtin_clz
,__builtin_clzl
et__builtin_clzll
fonctionnent bien pour les architectures 32 et 64 bits des nombres et à travers x86_64 et des machines x86. Cependant, trois fonctions sont nécessaires. J'ai trouvé un simple MSB, qui s'appuie sur le bouton droit de la maj qui va gérer tous les cas pour les nombres positifs. Au moins pour l'utilisation que j'allais en faire, il a réussi là où d'autres ont échoué:Par la désignation d'entrée comme
unsigned long long
il peut gérer tout le nombre de classes à partir deunsigned char
àunsigned long long
et compte tenu de la définition de la norme, il est compatible pour x86_64 et x86 construit. Le cas pour0
est défini pour revenir0
, mais peut être modifiée si besoin. Un simple test et de sortie sont:De sortie:
REMARQUE: pour des considérations relatives à la vitesse, à l'aide d'une seule fonction de faire la même chose centrée autour de
__builtin_clzll
est encore plus rapide d'un facteur de 6.Deux meilleures façons que je sais faire cela dans le plus pur C:
Première linéaire de recherche de l'octet/mot de tableau pour trouver le premier octet/mot qui est différente de zéro, faire un déroulé de binaires de recherche de l'octet/mot que vous trouverez.
3 (BTW c'est log2(8)), à la condition sauts pour obtenir la réponse. Sur moderne des machines x86 le dernier sera optimisé à une condition mov.
Vous pouvez également utiliser une table de recherche à la carte de l'octet à l'index du premier bit défini.
Un sujet connexe, vous voudrez peut-être regarder est entier log2 fonctions. Si je me souviens bien, ffmpeg a une belle mise en œuvre.
Edit: Vous pouvez réellement faire de la ci-dessus de recherche binaire dans un dépourvu de branches de recherche binaire, mais je ne sais pas si il serait plus efficace dans ce cas...
Pas le plus rapide, mais il fonctionne...
Voici un extrait de code expliquant __builtin_clozapine()
Je vais en ajouter un!
Bien sûr, c'est de travailler sur une version 64 bits (unsigned long long), et non pas un tableau. En outre, beaucoup de gens ont souligné intégré g++ fonctions que je n'étais pas au courant. Comment intéressant.
De toute façon, ce trouve le bit le plus significatif en 6 itérations et donne une assertion si vous êtes passés à 0 à la fonction. Pas la meilleure fonction à utiliser si vous avez accès à une instruction du chipset.
Moi aussi, je suis aussi à l'aide de |= au lieu de += car ce sont toujours des puissances de deux, et OU est (classique) plus rapide que l'addition. Depuis que je suis seulement en ajoutant des pouvoirs uniques de 2 ensemble, je n'ai jamais rouler.
C'est un binaire de recherche, ce qui signifie qu'il trouve toujours le résultat en 6 itérations.
Encore une fois, c'est mieux:
x86 a un BSR instruction qui renvoie un peu de l'index (plutôt que le nombre de zéros ci-dessus il).
Mais malheureusement il n'y a pas de portable intrinsèque que efficacement il expose pour tous les compilateurs. GNU C fournit
__builtin_clz
, maisunsigned bitidx = 31 - __builtin_clz(x);
n'a pas d'optimiser le retour à juste BSR avec courant de GCC et de la CPI. (Il le fait avec clang, ce qui prouve que l'expression est l'équivalent de sorte qu'il pourrait).Celui-ci définit
BSR32()
etBSR64()
des macros ou des fonctions que compiler efficacement à juste unbsr
d'instructions x86. (La production d'un des ordures résultat si l'entrée était de zéro. Il n'y a pas moyen avec intrinsèques pour prendre avantage de l'asm instruction du comportement de quitter la destination pas été modifiée depuis l'entrée=0.)La portabilité de non-x86 serait supplémentaires
#ifdef
par exemple, pour tomber à31-__builtin_clz
. La plupart des non-x86 Isa, si ils ont un avant-zéro bitscan à tous, le comte de zéros au lieu de vous donner le peu d'indice. C'est pourquoi GNU C définit__builtin_clz
que le portable intégré. (Si il n'y a pas de prise en charge de MATÉRIEL sur le système cible, le groupe builtin de compiler des logiciels d'émulation, généralement de l'appel d'une libgcc fonction d'assistance.)bsf
n'a probablement pas besoin d'autant d'aide pour les compilateurs, parce que le groupe builtin les matchs de l'asm instruction du comportement de retour de la bit-indice de la LSB, c'est à dire le nombre de zéros à droite.Un test en appelant
unsigned test32(unsigned x) { return BSR32(x); }
inlines à 1 instruction sur toutes les grandes x86 compilateurs, sur le Godbolt compilateur explorer. BSR64 inlines de la même manière, à une version 64 bits de l'opérande de taille la version. Voir aussi Est-il un x86/x86_64 instruction qui zéros tous les bits ci-dessous le Bit le Plus Significatif? par exemple des cas d'utilisation.Le point de ce est pour éviter de ralentir code du portable (pour les non-MSVC) version:
Sans
-march=haswell
nous a juste la BSR de bruit, mais:C'est juste méchant. (Intéressant de voir que la CPI est en train de faire un CMOV pour produire
-1
si l'entrée est égale à zéro. BSR ensembles ZF selon ses entrée, contrairement à la plupart des instructions de définir des indicateurs selon le résultat.)Avec
-march=haswell
(ou autre permettant l'utilisation de BMI1 instructions), il n'est pas aussi mauvais, mais toujours pas aussi bon que juste BSR. Modulo sortie de dépendances, qui compilateurs travaillent la plupart du temps à éviter pour lzcnt mais, curieusement, pas pour BSR. (D'où la sortie de la dépendance est un vrai de dépendance, en raison de l'entrée=0 comportement.) Pourquoi la rupture de la "sortie de la dépendance" de LZCNT question?Ici est un simple, la force brute de l'algorithme de l'arbitraire d'un tableau de taille d'octets:
Je vais le laisser comme un exercice pour le lecteur de se donner un
msb()
de la fonction ainsi que l'optimisation de travailler surint
oulong long
de la taille des interstices de données.De messagerie unifiée, votre balise indique 32bit mais il semble que les valeurs que vous utilisez sont de 16 bits. Si vous n'moyenne de 32 bits, alors je pense que la réponse à la 0x00a1 devrait être de 24 et pas 8.
En supposant que vous êtes à la recherche pour le MSB bit index de la main gauche côté et vous savez que vous ne serez en traitant avec uint32_t, voici ce qui est évident, simple d'esprit algorithme:
Pour java j'utilise ceci:
Et:
t
devrait probablement être entre parenthèses ici, si c'est une macro. ou encore mieux de le mettre dans une variable locale également, ainsi il n'est pas toujours calculé.