En C++ (y en C y en otros muchos lenguajes de programación) hay tres formas de escribir números enteros literales.
- En base 10 (decimal) cuando escribo algo como 1042348
- En base 16 (hexadecimal) cuando prefijo 0x como en 0x3FFF
- En base 8 (octal) cuando prefijo sólo 0 como en 0663
#define Ob(x) ((unsigned)Ob_(0 ## x ## uL)) #define Ob_(x) (x & 1 | x >> 2 & 2 | x >> 4 & 4 | x >> 6 & 8 | \ x >> 8 & 16 | x >> 10 & 32 | x >> 12 & 64 | x >> 14 & 128)
El truco se basa en dos pasos, el primero Ob (no es un cero, es una o) pone delante un 0 (que sí es un cero) y detrás un "uL". El "uL" es la forma que tenemos de decirle al C++ que el número es sin signo y largo. El cero, por otra parte, sí que es importante ya que nos va a convertir el número a octal. Lo que tenemos entonces es un número binario interpretado como un número octal. ¿Cómo afecta eso a su valor?
Un número binario se expresa de la siguiente forma [$$ n=\cdots + d_3 2^3 + d_2 2^2 + d_1 2^1 + d_0 2^0 $$] donde los [$d_i$] son los dígitos. Los dígitos serán o cero o uno. Al escribir el número binario como octal lo que hacemos es [$$ n=\cdots + d_3 8^3 + d_2 8^2 + d_1 8^1 + d_0 8^0 $$] pero aún con los dígitos en binario. Así que lo único que necesitamos es leer los bits de tres en tres ya que [$8=2^3$]. Justamente eso es lo que hace Ob_.
Por ejemplo:
Ob(1001) //Como binario es 9 en decimal
Ob_(01001uL) //Como octal es 513 en decimal y 1_000_000_001 en binario
9 //Tomando un bit de cada 3 en el 513