Quiero escribir una función que devuelva la siguiente potencia más cercana de 2 números. Por ejemplo, si mi entrada es 789, la salida debería ser 1024. ¿Hay alguna forma de lograr esto sin usar ningún bucle sino solo usando algunos operadores bit a bit?
c
optimization
bit-manipulation
Naveen
fuente
fuente
Respuestas:
Revisa los Bit Twiddling Hacks . Necesitas obtener el logaritmo de base 2, luego agrega 1 a eso. Ejemplo para un valor de 32 bits:
La extensión a otros anchos debería ser obvia.
fuente
uint64_t next_pow2(uint64_t x) { return x == 1 ? 1 : 1<<(64-__builtin_clzl(x-1)); }
Y por 32 bits:uint32_t next_pow2(uint32_t x) { return x == 1 ? 1 : 1<<(32-__builtin_clz(x-1)); }
Eso es si usa GCC (y Clang, creo), pero sería prudente tomarse el tiempo para encuentre la llamada a CLZ en lugar de copiar y pegar todas las opciones.x > UINT32_MAX
no tiene ramificaciones. Además, GCC y Clang usan-mtune=generic
de forma predeterminada (como lo hacen la mayoría de las distribuciones), por lo que su código NO se expandirá a laslzcnt
instrucciones en x86_64; en realidad se expandirá a algo MUCHO más lento (una rutina libgcc) a menos que use algo como-march=native
. Por lo tanto, su reemplazo propuesto no es portátil, tiene errores y (típicamente) es más lento.Esto funciona al encontrar el número por el que aumentaría 2 para obtener x (tome el registro del número y divida por el registro de la base deseada, consulte wikipedia para obtener más información ). Luego redondee eso con ceil para obtener la potencia numérica más cercana.
Este es un método de propósito más general (es decir, ¡más lento!) Que los métodos bit a bit vinculados a otros lugares, pero es bueno saber las matemáticas, ¿eh?
fuente
log(pow(2,29))/log(2)
= 29.000000000000004, por lo que el resultado es 2 30 en lugar de devolver 2 29. Creo que es por eso que existen las funciones log2?fuente
uint32_t
.Creo que esto también funciona:
Y la respuesta es
power
.fuente
power <<= 1
x
es demasiado grande (es decir, no hay suficientes bits para representar la próxima potencia de 2).Si está utilizando GCC, es posible que desee echar un vistazo a Optimización de la función next_pow2 () por lockless Inc .. Esta página describe una manera de utilizar función integrada
builtin_clz()
(cómputo cero inicial) y luego utilizar directamente x86 (IA32) instrucción del ensambladorbsr
(exploración de bits inversa), tal como se describe en el enlace de otra respuesta al sitio gamedev . Este código puede ser más rápido que los descritos en la respuesta anterior .Por cierto, si no vas a usar instrucciones de ensamblador y tipo de datos de 64 bits, puedes usar esto
fuente
_BitScanForward
en Visual C ++__builtin_ctz()
__builtin_ctz()
no será útil para redondear cualquier número que no sea potencia de 2 hasta la próxima potencia de dosconstexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL<<(sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
Uno más, aunque uso el ciclo, pero esto es mucho más rápido que los operandos matemáticos
potencia de dos opciones de "piso":
potencia de dos opciones "ceil":
ACTUALIZAR
Como se mencionó en los comentarios, hubo un error en el
ceil
resultado incorrecto.Aquí hay funciones completas:
fuente
x
potencia es de 2. Se necesita un micro para probar si la entrada es potencia de 2.#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
no es correcto. Por ejemplo, cuandox = 2
el resultado debería ser en2
lugar de4
Para cualquier tipo sin firmar, basándose en Bit Twiddling Hacks:
Realmente no hay un bucle allí, ya que el compilador sabe en tiempo de compilación el número de iteraciones.
fuente
std::is_unsigned<UnsignedType>::value
afirmación.Para las carrozas IEEE, podrías hacer algo como esto.
Si necesita una solución entera y puede usar el ensamblaje en línea, BSR le dará el log2 de un entero en el x86. Cuenta cuántos bits correctos se establecen, que es exactamente igual al log2 de ese número. Otros procesadores tienen instrucciones similares (a menudo), como CLZ y, dependiendo de su compilador, puede haber un intrínseco disponible para hacer el trabajo por usted.
fuente
A pesar de que la pregunta está etiquetada
c
aquí, mis cinco centavos. Por suerte, C ++ 20 incluiríastd::ceil2
ystd::floor2
(ver aquí ). Sonconsexpr
funciones de plantilla, la implementación actual de GCC usa desplazamiento de bits y funciona con cualquier tipo integral sin signo.fuente
bit_ceil
open-std.org/JTC1/SC22/WG21/docs/papers/2020/p1956r1.pdfSi no desea aventurarse en el ámbito del comportamiento indefinido, el valor de entrada debe estar entre 1 y 2 ^ 63. La macro también es útil para establecer constante en tiempo de compilación.
fuente
Para completar, aquí hay una implementación de punto flotante en bog-standard C.
fuente
rep bsr ecx,eax; mov eax,0; cmovnz eax,2; shl eax,cl
es aproximadamente 25 veces más rápido.Una solución específica eficiente de Microsoft (por ejemplo, Visual Studio 2017) en C / C ++ para la entrada de enteros. Maneja el caso de la entrada que coincide exactamente con una potencia de dos valores disminuyendo antes de verificar la ubicación del 1 bit más significativo.
Esto genera 5 o más instrucciones en línea para un procesador Intel similar al siguiente:
Aparentemente, el compilador de Visual Studio C ++ no está codificado para optimizar esto para valores de tiempo de compilación, pero no es que haya muchas instrucciones allí.
Editar:
Si desea que un valor de entrada de 1 produzca 1 (2 a la potencia cero), una pequeña modificación al código anterior aún genera instrucciones directas sin ramificación.
Genera solo unas pocas instrucciones más. El truco es que Index puede ser reemplazado por una prueba seguida de una instrucción cmove.
fuente
En x86 puede usar las instrucciones de manipulación de bits sse4 para hacerlo más rápido.
En c puede usar las intrínsecas coincidentes.
fuente
Aquí está mi solución en C. ¡Espero que esto ayude!
fuente
Muchas arquitecturas de procesador admiten
log base 2
u operaciones muy similarescount leading zeros
. Muchos compiladores tienen intrínsecos para ello. Ver https://en.wikipedia.org/wiki/Find_first_setfuente
Asumiendo que tienes un buen compilador y que puede hacer un poco de tonterías antes de eso, en este punto, ¡pero de todos modos esto funciona!
Código de prueba a continuación:
Salidas:
fuente
Estoy tratando de obtener la potencia inferior más cercana de 2 e hice esta función. Que te ayude. Simplemente multiplica el número inferior más cercano por 2 para obtener la potencia superior más cercana de 2
fuente
Respuesta adaptada de Paul Dixon a Excel, esto funciona perfectamente.
fuente
Una variante de la respuesta @YannDroneaud válida para
x==1
, solo para plataformas x86, compiladores, gcc o clang:fuente
Esto es lo que estoy usando para que esta sea una expresión constante, si la entrada es una expresión constante.
Entonces, por ejemplo, una expresión como:
se reducirá muy bien a una constante.
fuente
Puede encontrar la siguiente aclaración útil para su propósito:
fuente
Conviértalo a flotante y luego use .hex () que muestra la representación IEEE normalizada.
>>> float(789).hex() '0x1.8a80000000000p+9'
Luego solo extraiga el exponente y agregue 1.
>>> int(float(789).hex().split('p+')[1]) + 1 10
Y elevar 2 a este poder.
>>> 2 ** (int(float(789).hex().split('p+')[1]) + 1) 1024
fuente
fuente
Si lo necesita para cosas relacionadas con OpenGL:
fuente
Si quieres una plantilla de una línea. Aquí está
o
fuente
n
varias veces sin un punto de secuencia no es válido. Lo escribió como sin-=1
fuera a suceder primero, pero la única garantía aquí es quen
contiene su nuevo valor después de;
y los paréntesis no cambian eso.