Je veux écrire une fonction qui renvoie la puissance suivante la plus proche de 2 nombre. Par exemple, si mon entrée est 789, la sortie doit être 1024. Existe-t-il un moyen d'y parvenir sans utiliser de boucles mais en utilisant simplement des opérateurs au niveau du bit?
c
optimization
bit-manipulation
Naveen
la source
la source
Réponses:
Vérifiez les Bit Twiddling Hacks . Vous devez obtenir le logarithme de base 2, puis ajouter 1 à cela. Exemple pour une valeur 32 bits:
L'extension à d'autres largeurs devrait être évidente.
la source
uint64_t next_pow2(uint64_t x) { return x == 1 ? 1 : 1<<(64-__builtin_clzl(x-1)); }
Et pour 32 bits:uint32_t next_pow2(uint32_t x) { return x == 1 ? 1 : 1<<(32-__builtin_clz(x-1)); }
c'est si vous utilisez GCC (et Clang je pense?), Mais il serait sage de prendre le temps de trouver l'appel à CLZ au lieu de copier-coller toutes les options autour.x > UINT32_MAX
et n'est pas sans branche. De plus, GCC et Clang utilisent-mtune=generic
par défaut (comme le font la plupart des distributions), donc votre code ne sera PAS étendu à l'lzcnt
instruction sur x86_64 - il sera en fait étendu à quelque chose de BEAUCOUP plus lent (une routine libgcc) à moins que vous n'utilisiez quelque chose comme-march=native
. Donc, votre remplacement proposé est non portable, bogué et (généralement) plus lent.Cela fonctionne en trouvant le nombre que vous auriez augmenté de 2 pour obtenir x (prenez le journal du nombre et divisez par le journal de la base souhaitée, voir wikipedia pour plus ). Arrondissez ensuite avec ceil pour obtenir la puissance du nombre entier le plus proche.
C'est une méthode plus générale (c'est-à-dire plus lente!) Que les méthodes au niveau du bit liées ailleurs, mais il est bon de connaître les mathématiques, hein?
la source
log(pow(2,29))/log(2)
= 29.000000000000004, donc le résultat est 2 30 au lieu de renvoyer 2 29. Je pense que c'est pourquoi les fonctions log2 existent?la source
uint32_t
.Je pense que cela fonctionne aussi:
Et la réponse est
power
.la source
power <<= 1
x
est trop grande (c'est-à-dire pas assez de bits pour représenter la prochaine puissance de 2).Si vous utilisez GCC, vous voudrez peut-être jeter un oeil à Optimisation de la fonction next_pow2 () par Lockless Inc .. Cette page décrit un moyen d'utiliser la fonction intégrée
builtin_clz()
(nombre de zéro devant ) et plus tard d'utiliser directement x86 (ia32) instruction assembleurbsr
(bit scan reverse), comme décrit dans le lien d' une autre réponse vers le site gamedev . Ce code peut être plus rapide que ceux décrits dans la réponse précédente .À propos, si vous n'allez pas utiliser l'instruction assembleur et le type de données 64 bits, vous pouvez utiliser ceci
la source
_BitScanForward
sur Visual C ++__builtin_ctz()
__builtin_ctz()
ne sera pas utile pour arrondir toute puissance de 2 à la prochaine puissance de deuxconstexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
Un de plus, bien que j'utilise le cycle, mais c'est beaucoup plus rapide que les opérandes mathématiques
puissance de deux option "étage":
option puissance de deux "ceil":
METTRE À JOUR
Comme mentionné dans les commentaires, il y a eu une erreur dans le
ceil
cas où son résultat était faux.Voici les fonctions complètes:
la source
x
puissance est de 2. Un micro pour tester si l'entrée est une puissance de 2 est nécessaire.#define ISPOW2(x) ((x) > 0 && !((x) & (x-1)))
if (x == 0) return 1; /* Or 0 (Which is what I use) */ x--; /* Rest of program */
power of two "ceil" option
n'est pas correct. Par exemple, lorsquex = 2
le résultat doit être2
au lieu de4
Pour tout type non signé, en s'appuyant sur les Bit Twiddling Hacks:
Il n'y a pas vraiment de boucle car le compilateur connaît au moment de la compilation le nombre d'itérations.
la source
std::is_unsigned<UnsignedType>::value
assertion.Pour les flotteurs IEEE, vous pourriez faire quelque chose comme ça.
Si vous avez besoin d'une solution entière et que vous êtes capable d'utiliser l'assemblage en ligne, BSR vous donnera le log2 d'un entier sur le x86. Il compte combien de bits droits sont définis, ce qui est exactement égal au log2 de ce nombre. D'autres processeurs ont des instructions similaires (souvent), telles que CLZ et en fonction de votre compilateur, il peut y avoir un intrinsèque disponible pour faire le travail à votre place.
la source
Malgré la question est étiqueté comme
c
ici mes cinq cents. Heureusement pour nous, C ++ 20 incluraitstd::ceil2
etstd::floor2
(voir ici ). Ce sont desconsexpr
fonctions de modèle, l' implémentation actuelle de GCC utilise le décalage de bits et fonctionne avec tout type non signé intégral.la source
bit_ceil
open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdfSi vous ne voulez pas vous aventurer dans le domaine du comportement indéfini, la valeur d'entrée doit être comprise entre 1 et 2 ^ 63. La macro est également utile pour définir une constante au moment de la compilation.
la source
Pour être complet, voici une implémentation en virgule flottante dans la norme bog C.
la source
rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl
est environ 25 fois plus rapide.Une solution efficace spécifique à Microsoft (par exemple, Visual Studio 2017) en C / C ++ pour la saisie d'entiers. Gère le cas de l'entrée correspondant exactement à une valeur de puissance de deux en décrémentant avant de vérifier l'emplacement du bit 1 le plus significatif.
Cela génère environ 5 instructions en ligne pour un processeur Intel similaire à ce qui suit:
Apparemment, le compilateur Visual Studio C ++ n'est pas codé pour optimiser cela pour les valeurs de compilation, mais ce n'est pas comme s'il y avait beaucoup d'instructions.
Éditer:
Si vous voulez qu'une valeur d'entrée de 1 donne 1 (2 à la puissance zéro), une petite modification du code ci-dessus génère toujours des instructions directes sans branche.
Génère juste quelques instructions supplémentaires. L'astuce est que Index peut être remplacé par un test suivi d'une instruction cmove.
la source
Dans x86, vous pouvez utiliser les instructions de manipulation de bits sse4 pour le rendre rapide.
En c, vous pouvez utiliser les intrinsèques correspondants.
la source
Voici ma solution en C. J'espère que cela vous aidera!
la source
De nombreuses architectures de processeur prennent en charge
log base 2
ou un fonctionnement très similaire -count leading zeros
. De nombreux compilateurs ont des caractéristiques intrinsèques. Voir https://en.wikipedia.org/wiki/Find_first_setla source
En supposant que vous ayez un bon compilateur et qu'il puisse faire le petit twiddling avant la main, c'est au-dessus de moi à ce stade, mais de toute façon cela fonctionne !!!
Code de test ci-dessous:
Les sorties:
la source
J'essaie d'obtenir la puissance inférieure la plus proche de 2 et j'ai fait cette fonction. Que cela vous aide, il suffit de multiplier le nombre inférieur le plus proche par 2 pour obtenir la puissance supérieure la plus proche de 2
la source
Adapté de la réponse de Paul Dixon à Excel, cela fonctionne parfaitement.
la source
Une variante de @YannDroneaud répond valable pour
x==1
, uniquement pour les plates-formes x86, les compilateurs, gcc ou clang:la source
Voici ce que j'utilise pour que ce soit une expression constante, si l'entrée est une expression constante.
Ainsi, par exemple, une expression comme:
se réduira joliment à une constante.
la source
Vous trouverez peut-être la clarification suivante utile à votre objectif:
la source
Convertissez-le en flottant, puis utilisez .hex () qui montre la représentation IEEE normalisée.
>>> float(789).hex() '0x1.8a80000000000p+9'
Ensuite, extrayez simplement l'exposant et ajoutez 1.
>>> int(float(789).hex().split('p+')[1]) + 1 10
Et élevez 2 à cette puissance.
>>> 2 ** (int(float(789).hex().split('p+')[1]) + 1) 1024
la source
la source
Si vous en avez besoin pour des choses liées à OpenGL:
la source
Si vous voulez un modèle sur une ligne. C'est ici
ou
la source
n
plusieurs reprises sans point de séquence n'est pas valide. Vous l'avez écrit comme sin-=1
cela devait arriver en premier, mais la seule garantie ici est quen
contient sa nouvelle valeur après le;
et les parenthèses ne changent pas cela.