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.
inline unsigned int ExpandToPowerOf2(unsigned int Value)
{
unsigned long Index;
_BitScanReverse(&Index, Value - 1);
return (1U << (Index + 1));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#if defined(WIN64) // The _BitScanReverse64 intrinsic is only available for 64 bit builds because it depends on x64
inline unsigned long long ExpandToPowerOf2(unsigned long long Value)
{
unsigned long Index;
_BitScanReverse64(&Index, Value - 1);
return (1ULL << (Index + 1));
}
#endif
Cela génère environ 5 instructions en ligne pour un processeur Intel similaire à ce qui suit:
dec eax
bsr rcx, rax
inc ecx
mov eax, 1
shl rax, cl
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.
inline unsigned int ExpandToPowerOf2(unsigned int Value)
{
unsigned long Index;
_BitScanReverse(&Index, --Value);
if (Value == 0)
Index = (unsigned long) -1;
return (1U << (Index + 1));
}
Génère juste quelques instructions supplémentaires. L'astuce est que Index peut être remplacé par un test suivi d'une instruction cmove.