c++ - programacion - Literales binarios?
literales en programacion (17)
Como un aparte:
Especialmente si se trata de un conjunto grande, en lugar de pasar por el esfuerzo mental [menor] de escribir una secuencia de cantidades de desplazamiento, puede hacer que cada constante dependa de la constante definida anteriormente:
const int has_nukes = 1;
const int has_bio_weapons = has_nukes << 1;
const int has_chem_weapons = has_bio_weapons << 1;
const int has_nunchuks = has_chem_weapons << 1;
// ...
Parece un poco redundante, pero es menos propenso a errores tipográficos. Además, puede simplemente insertar una nueva constante en el medio sin tener que tocar ninguna otra línea, excepto la que le sigue inmediatamente:
const int has_nukes = 1;
const int has_gravity_gun = has_nukes << 1; // added
const int has_bio_weapons = has_gravity_gun << 1; // changed
const int has_chem_weapons = has_bio_weapons << 1; // unaffected from here on
const int has_nunchuks = has_chem_weapons << 1;
// ...
Comparar con:
const int has_nukes = 1 << 0;
const int has_bio_weapons = 1 << 1;
const int has_chem_weapons = 1 << 2;
const int has_nunchuks = 1 << 3;
// ...
const int has_scimatar = 1 << 28;
const int has_rapier = 1 << 28; // good luck spotting this typo!
const int has_katana = 1 << 30;
Y:
const int has_nukes = 1 << 0;
const int has_gravity_gun = 1 << 1; // added
const int has_bio_weapons = 1 << 2; // changed
const int has_chem_weapons = 1 << 3; // changed
const int has_nunchuks = 1 << 4; // changed
// ... // changed all the way
const int has_scimatar = 1 << 29; // changed *sigh*
const int has_rapier = 1 << 30; // changed *sigh*
const int has_katana = 1 << 31; // changed *sigh*
Aparte de mi lado, probablemente sea igualmente difícil detectar un error tipográfico como este:
const int has_nukes = 1;
const int has_gravity_gun = has_nukes << 1;
const int has_bio_weapons = has_gravity_gun << 1;
const int has_chem_weapons = has_gravity_gun << 1; // oops!
const int has_nunchuks = has_chem_weapons << 1;
Por lo tanto, creo que la principal ventaja de esta sintaxis en cascada es cuando se trata de inserciones y eliminaciones de constantes.
En el código, a veces veo que las personas especifican constantes en formato hexadecimal como este:
const int has_nukes = 0x0001;
const int has_bio_weapons = 0x0002;
const int has_chem_weapons = 0x0004;
// ...
int arsenal = has_nukes | has_bio_weapons | has_chem_weapons; // all of them
if(arsenal &= has_bio_weapons){
std::cout << "BIO!!"
}
Pero no tiene sentido para mí usar el formato hexadecimal aquí. ¿Hay alguna forma de hacerlo directamente en binario? Algo como esto:
const int has_nukes = 0b00000000000000000000000000000001;
const int has_bio_weapons = 0b00000000000000000000000000000010;
const int has_chem_weapons = 0b00000000000000000000000000000100;
// ...
Sé que los compiladores C / C ++ no compilarán esto, pero debe haber una solución alternativa? ¿Es posible en otros lenguajes como Java?
El término que quieres son literales binarios
Ruby los tiene con la sintaxis que le das.
Una alternativa es definir macros de ayuda para convertir para usted. Encontré el siguiente código en http://bytes.com/groups/c/219656-literal-binary
/* Binary constant generator macro
* By Tom Torfs - donated to the public domain
*/
/* All macro''s evaluate to compile-time constants */
/* *** helper macros *** */
/* turn a numeric literal into a hex constant
* (avoids problems with leading zeroes)
* 8-bit constants max value 0x11111111, always fits in unsigned long
*/
#define HEX_(n) 0x##n##LU
/* 8-bit conversion function */
#define B8_(x) ((x & 0x0000000FLU) ? 1:0) /
| ((x & 0x000000F0LU) ? 2:0) /
| ((x & 0x00000F00LU) ? 4:0) /
| ((x & 0x0000F000LU) ? 8:0) /
| ((x & 0x000F0000LU) ? 16:0) /
| ((x & 0x00F00000LU) ? 32:0) /
| ((x & 0x0F000000LU) ? 64:0) /
| ((x & 0xF0000000LU) ? 128:0)
/* *** user macros *** /
/* for upto 8-bit binary constants */
#define B8(d) ((unsigned char) B8_(HEX_(d)))
/* for upto 16-bit binary constants, MSB first */
#define B16(dmsb, dlsb) (((unsigned short) B8(dmsb) << 8) /
| B8(dlsb))
/* for upto 32-bit binary constants, MSB first */
#define B32(dmsb, db2, db3, dlsb) (((unsigned long) B8(dmsb) << 24) /
| ((unsigned long) B8( db2) << 16) /
| ((unsigned long) B8( db3) << 8) /
| B8(dlsb))
/* Sample usage:
* B8(01010101) = 85
* B16(10101010,01010101) = 43605
* B32(10000000,11111111,10101010,01010101) = 2164238933
*/
En C ++ 14, podrá usar literales binarios con la siguiente sintaxis:
0b010101010 /* more zeros and ones */
Esta característica ya está implementada en los últimos clang
y gcc
. Puede intentarlo si ejecuta esos compiladores con la -std=c++1y
.
Escribo literales binarios como este:
const int has_nukes = 0x0001;
const int has_bio_weapons = 0x0002;
const int has_chem_weapons = 0x0004;
Es más compacto que la notación sugerida y más fácil de leer. Por ejemplo:
const int upper_bit = 0b0001000000000000000;
versus:
const int upper_bit = 0x04000;
¿Notaste que la versión binaria no era un múltiplo par de 4 bits? ¿Pensaste que era 0x10000?
Con un poco de práctica, el hexágono u octal es más fácil para un ser humano que binario. Y, en mi opinión, es más fácil de leer que usar operadores de turno. Pero concedo que mis años de trabajo en lenguaje ensamblador pueden sesgarme en ese punto.
Estoy de acuerdo en que es útil tener una opción para literales binarios, y están presentes en muchos lenguajes de programación. En C, he decidido usar una macro como esta:
#define bitseq(a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, /
a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) /
(a31|a30<< 1|a29<< 2|a28<< 3|a27<< 4|a26<< 5|a25<< 6|a24<< 7| /
a23<< 8|a22<< 9|a21<<10|a20<<11|a19<<12|a18<<13|a17<<14|a16<<15| /
a15<<16|a14<<17|a13<<18|a12<<19|a11<<20|a10<<21|a09<<22|a08<<23| /
a07<<24|a06<<25|a05<<26|a04<<27|a03<<28|a02<<29|a01<<30|(unsigned)a00<<31)
El uso es bastante directo =)
GCC admite constantes binarias como una extensión desde 4.3. Vea el announcement (mire la sección "Nuevos idiomas y mejoras específicas del idioma").
Java tampoco admite literales binarios, desafortunadamente. Sin embargo, tiene enums que se pueden usar con un EnumSet
. Un EnumSet
representa valores enum internamente con campos de bits, y presenta una interfaz Set
para manipular estos indicadores.
Alternativamente, puede usar desplazamientos de bits (en decimal) al definir sus valores:
const int HAS_NUKES = 0x1 << 0;
const int HAS_BIO_WEAPONS = 0x1 << 1;
const int HAS_CHEM_WEAPONS = 0x1 << 2;
La biblioteca estándar de C ++ es tu amiga:
#include <bitset>
const std::bitset <32> has_nukes( "00000000000000000000000000000001" );
La próxima versión de C ++, C ++ 0x, introducirá literales definidos por el usuario . No estoy seguro de si los números binarios serán parte del estándar, pero en el peor de los casos podrá habilitarlo usted mismo:
int operator "" _B(int i);
assert( 1010_B == 10);
No hay sintaxis para las constantes binarias literales en C ++ de la misma forma que para hexadecimal y octal. Lo más parecido a lo que parece que estás tratando de hacer sería aprender y usar bitset .
Otro método:
template<unsigned int N>
class b
{
public:
static unsigned int const x = N;
typedef b_<0> _0000;
typedef b_<1> _0001;
typedef b_<2> _0010;
typedef b_<3> _0011;
typedef b_<4> _0100;
typedef b_<5> _0101;
typedef b_<6> _0110;
typedef b_<7> _0111;
typedef b_<8> _1000;
typedef b_<9> _1001;
typedef b_<10> _1010;
typedef b_<11> _1011;
typedef b_<12> _1100;
typedef b_<13> _1101;
typedef b_<14> _1110;
typedef b_<15> _1111;
private:
template<unsigned int N2>
struct b_: public b<N << 4 | N2> {};
};
typedef b<0> _0000;
typedef b<1> _0001;
typedef b<2> _0010;
typedef b<3> _0011;
typedef b<4> _0100;
typedef b<5> _0101;
typedef b<6> _0110;
typedef b<7> _0111;
typedef b<8> _1000;
typedef b<9> _1001;
typedef b<10> _1010;
typedef b<11> _1011;
typedef b<12> _1100;
typedef b<13> _1101;
typedef b<14> _1110;
typedef b<15> _1111;
Uso:
std::cout << _1101::_1001::_1101::_1101::x;
Implementado en CityLizard ++ (citylizard / binary / b.hpp) .
Por cierto, la próxima versión de C ++ admitirá literales definidos por el usuario. Ya están incluidos en el borrador de trabajo. Esto permite ese tipo de cosas (ojalá no tenga demasiados errores):
template<char... digits>
constexpr int operator "" _b() {
return conv2bin<digits...>::value;
}
int main() {
int const v = 110110110_b;
}
conv2bin
sería una plantilla como esta:
template<char... digits>
struct conv2bin;
template<char high, char... digits>
struct conv2bin<high, digits...> {
static_assert(high == ''0'' || high == ''1'', "no bin num!");
static int const value = (high - ''0'') * (1 << sizeof...(digits)) +
conv2bin<digits...>::value;
};
template<char high>
struct conv2bin<high> {
static_assert(high == ''0'' || high == ''1'', "no bin num!");
static int const value = (high - ''0'');
};
Bueno, lo que obtenemos son literales binarios que ya se evalúan completamente en tiempo de compilación, debido al "constexpr" anterior. Lo anterior usa un tipo de retorno int duro. Creo que uno podría incluso hacer depender la longitud de la cadena binaria. Está utilizando las siguientes características, para cualquier persona interesada:
- Expresiones constantes generalizadas.
- Plantillas variables Una breve introducción se puede encontrar here
- Afirmaciones estáticas (static_assert)
- Literals definidos por el usuario
En realidad, el tronco actual de GCC already implementa plantillas variadas y aserciones estáticas. Esperemos que apoye a los otros dos pronto. Creo que C ++ 1x sacudirá la casa.
Puedes usar << si quieres.
int hasNukes = 1;
int hasBioWeapons = 1 << 1;
int hasChemWeapons = 1 << 2;
Si desea utilizar conjuntos de bits, auto, plantillas variadic, literales definidos por el usuario, static_assert, constexpr y noexcept intente esto:
template<char... Bits>
struct __checkbits
{
static const bool valid = false;
};
template<char High, char... Bits>
struct __checkbits<High, Bits...>
{
static const bool valid = (High == ''0'' || High == ''1'')
&& __checkbits<Bits...>::valid;
};
template<char High>
struct __checkbits<High>
{
static const bool valid = (High == ''0'' || High == ''1'');
};
template<char... Bits>
inline constexpr std::bitset<sizeof...(Bits)>
operator"" bits() noexcept
{
static_assert(__checkbits<Bits...>::valid, "invalid digit in binary string");
return std::bitset<sizeof...(Bits)>((char []){Bits..., ''/0''});
}
Úselo así:
int
main()
{
auto bits = 0101010101010101010101010101010101010101010101010101010101010101bits;
std::cout << bits << std::endl;
std::cout << "size = " << bits.size() << std::endl;
std::cout << "count = " << bits.count() << std::endl;
std::cout << "value = " << bits.to_ullong() << std::endl;
// This triggers the static_assert at compile-time.
auto badbits = 2101010101010101010101010101010101010101010101010101010101010101bits;
// This throws at run-time.
std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101bits");
}
Gracias a @ johannes-schaub-litb
Una manera ligeramente horrible de hacerlo es generando un archivo .h con muchas #defines ...
#define b00000000 0
#define b00000001 1
#define b00000010 2
#define b00000011 3
#define b00000100 4
etc. Esto podría tener sentido para números de 8 bits, pero probablemente no para 16 bits o más.
Alternativamente, haz esto (similar a la respuesta de Zach Scrivena):
#define bit(x) (1<<x)
int HAS_NUKES = bit(HAS_NUKES_OFFSET);
int HAS_BIO_WEAPONS = bit(HAS_BIO_WEAPONS_OFFSET);
Yo usaría un operador de desplazamiento de bits:
const int has_nukes = 1<<0;
const int has_bio_weapons = 1<<1;
const int has_chem_weapons = 1<<2;
// ...
int dangerous_mask = has_nukes | has_bio_weapons | has_chem_weapons;
bool is_dangerous = (country->flags & dangerous_mask) == dangerous_mask;
Es incluso mejor que una inundación de 0.
Esta discusión puede ser interesante ... Podría haber sido, ya que el enlace está muerto por desgracia. Describió un enfoque basado en plantillas similar a otras respuestas aquí.
Y también hay una cosa llamada BOOST_BINARY .