Literales binarios C #

187

¿Hay alguna manera de escribir literales binarios en C #, como prefijar hexadecimal con 0x? 0b no funciona.

Si no, ¿cuál es una manera fácil de hacerlo? ¿Algún tipo de conversión de cadena?

toxvaerd
fuente
2
Usa constantes y dales nombres propios, entonces el hecho de que tengas que escribirlas en decimal no debería importar. Personalmente, me gustaría leer en if (a == MaskForModemIOEnabled)lugar deif (a == 0b100101)
Lasse V. Karlsen
2
Otra nota: si encontró esta publicación porque desea un campo de bits, considere el atributo flags: msdn.microsoft.com/en-us/library/cc138362.aspx#sectionToggle0
toxvaerd
17
Ambos son muy buenos consejos, excepto cuando se definen dichos campos de bits, donde estas definiciones constantes con nombre pueden ser más fáciles de leer y escribir con un literal binario. [Indicadores] enum Cantidad {Ninguno = 0, Uno = 1, Algunos = 0b10, La mayoría = 0b100, Todos = 0b111}
brianary
1
Para lo que vale, el soporte para literales binarios está planeado para Roslyn .
Joe White el
Todavía no es compatible con C # 6.0, ¿verdad? No se pueden usar literales binarios en vs2015: /
anhoppe

Respuestas:

146

C # 7.0 admite literales binarios (y separadores de dígitos opcionales mediante caracteres de subrayado).

Un ejemplo:

int myValue = 0b0010_0110_0000_0011;

También puede encontrar más información en la página de Roslyn GitHub .

BTownTKD
fuente
3
@ D.Singh Lo veo en la lista C # 7 ahora. github.com/dotnet/roslyn/issues/2136
Danza
9
¡Dulce! Funciona muy bien en VS2017 / C # 7.
STLDev
1
Es útil recordar que los literales binarios de C # son big-endian, pero en x86 los enteros son little-endian, por Int16 = 0b0010_0110_0000_0011lo que se almacenarán como { 0b0000_0011, 0b0010_0110 }confusos.
Dai
1
@Dai no es diferente de los literales hexadecimales. En otras palabras, todas las constantes son big endian, pero se almacenan little endian en Intel / AMD y la mayoría de los ARM. 0xDEADBEEFserá almacenado como0xEF 0xBE 0xAD 0xDE
Cole Johnson
170

Actualizar

C # 7.0 ahora tiene literales binarios, lo cual es increíble.

[Flags]
enum Days
{
    None = 0,
    Sunday    = 0b0000001,
    Monday    = 0b0000010,   // 2
    Tuesday   = 0b0000100,   // 4
    Wednesday = 0b0001000,   // 8
    Thursday  = 0b0010000,   // 16
    Friday    = 0b0100000,   // etc.
    Saturday  = 0b1000000,
    Weekend = Saturday | Sunday,
    Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday
}

Publicación original

Dado que el tema parece haberse convertido en declarar valores de bandera basados ​​en bits en enumeraciones, pensé que valdría la pena señalar un truco útil para este tipo de cosas. El operador de desplazamiento a la izquierda ( <<) le permitirá empujar un poco a una posición binaria específica. Combine eso con la capacidad de declarar valores de enumeración en términos de otros valores en la misma clase, y tendrá una sintaxis declarativa muy fácil de leer para enumeraciones de marcas de bits.

[Flags]
enum Days
{
    None        = 0,
    Sunday      = 1,
    Monday      = 1 << 1,   // 2
    Tuesday     = 1 << 2,   // 4
    Wednesday   = 1 << 3,   // 8
    Thursday    = 1 << 4,   // 16
    Friday      = 1 << 5,   // etc.
    Saturday    = 1 << 6,
    Weekend     = Saturday | Sunday,
    Weekdays    = Monday | Tuesday | Wednesday | Thursday | Friday
}
StriplingWarrior
fuente
26
@ColeJohnson: Muchos desarrolladores prefieren eso. No soy tan bueno para convertir hexadecimal en mi cabeza como lo son algunos desarrolladores, y a veces es mejor atender al mínimo común denominador.
StriplingWarrior
2
Creo que esta es la forma más efectiva ya que las enumeraciones se crean como constantes. Con el atributo opcional [Flags] pueden usarse en operaciones bit a bit (no directamente relacionadas con la pregunta, pero particularmente útiles cuando se describen literales binarios). Otra posibilidad interesante es forzar el tipo de enumeración como un tipo incorporado (en este ejemplo, agregue ': byte' después de 'Días'); ver tipos incorporados en bit.ly/MKv42E
caligari
En realidad, eso es muy fácil de leer, aunque declarar una
marca de
55
@MikeT: ¿Puedes elaborar (o proporcionar un enlace que explique) en ese último pensamiento? A menudo declaro enumeraciones que comienzan con "1" porque quiero poder detectar y fallar rápidamente cuando un valor simplemente nunca se inicializó. Pero hay algunas situaciones en las que un valor predeterminado realmente tiene sentido, y creo que no habría nada de malo en agregar un nombre para ese valor.
StriplingWarrior
3
@MikeT De ca1008 : "Si una enumeración que tiene el atributo FlagsAttribute aplicado define un miembro de valor cero, su nombre debe ser 'Ninguno' para indicar que no se han establecido valores en la enumeración". Por lo tanto, es perfectamente válido agregarlo para que sirva como valor predeterminado, si se llama "Ninguno". En general, me parece más claro si el valor en un campo inicializado por defecto realmente tiene un nombre para mostrar.
Nyerguds
115

Solo entero y hexadecimal directamente, me temo (ECMA 334v4):

9.4.4.2 Literales enteros Los literales enteros se utilizan para escribir valores de los tipos int, uint, long y ulong. Los literales enteros tienen dos formas posibles: decimal y hexadecimal.

Para analizar, puede usar:

int i = Convert.ToInt32("01101101", 2);
Marc Gravell
fuente
44
Como una actualización de esta respuesta, con la última información moderna de vanguardia (julio de 2014); C # 6.0 introduce literales binarios. Vea mi respuesta actualizada wayyyyyy en la parte inferior ...
BTownTKD
1
@BTownTKD su respuesta está realmente en la parte superior :), ya que es la respuesta aceptada.
RBT
25

Agregando a la respuesta de @ StriplingWarrior sobre las banderas de bits en las enumeraciones, hay una convención fácil que puede usar en hexadecimal para contar hacia arriba a través de los cambios de bits. Use la secuencia 1-2-4-8, mueva una columna a la izquierda y repita.

[Flags]
enum Scenery
{
  Trees   = 0x001, // 000000000001
  Grass   = 0x002, // 000000000010
  Flowers = 0x004, // 000000000100
  Cactus  = 0x008, // 000000001000
  Birds   = 0x010, // 000000010000
  Bushes  = 0x020, // 000000100000
  Shrubs  = 0x040, // 000001000000
  Trails  = 0x080, // 000010000000
  Ferns   = 0x100, // 000100000000
  Rocks   = 0x200, // 001000000000
  Animals = 0x400, // 010000000000
  Moss    = 0x800, // 100000000000
}

Escanee hacia abajo comenzando con la columna derecha y observe el patrón 1-2-4-8 (turno) 1-2-4-8 (turno) ...


Para responder a la pregunta original, secundo la sugerencia de @ Sahuagin de usar literales hexadecimales. Si está trabajando con números binarios con la suficiente frecuencia como para que esto sea una preocupación, vale la pena aprender el hexadecimal.

Si necesita ver números binarios en el código fuente, sugiero agregar comentarios con literales binarios como los que he mencionado anteriormente.

Roy Tinker
fuente
23

Siempre puede crear cuasi literales, constantes que contengan el valor que busca:

const int b001 = 1;
const int b010 = 2;
const int b011 = 3;
// etc ...
Debug.Assert((b001 | b010) == b011);

Si los usa con frecuencia, puede envolverlos en una clase estática para su reutilización.

Sin embargo, un poco fuera de tema, si tiene alguna semántica asociada con los bits (conocida en tiempo de compilación) sugeriría usar una Enum en su lugar:

enum Flags
{ 
    First = 0,
    Second = 1,
    Third = 2,
    SecondAndThird = 3
}
// later ...
Debug.Assert((Flags.Second | Flags.Third) == Flags.SecondAndThird);
Markus Johnsson
fuente
18

Si observa el estado de implementación de la función de lenguaje de .NET Compiler Platform ("Roslyn"), puede ver claramente que en C # 6.0 esta es una función planificada, por lo que en la próxima versión podemos hacerlo de la manera habitual.

Estado literal binario

totymedli
fuente
3
¿Sucedió? No todas las características sobre las que se ha publicado en el blog realmente hicieron el lanzamiento de Visual Studio 2015.
Coronel Panic
44
@ColonelPanic - Danation señala que está "en la lista C # 7 ahora" - github.com/dotnet/roslyn/issues/2136
Wai Ha Lee
3
string sTable="static class BinaryTable\r\n{";
string stemp = "";
for (int i = 0; i < 256; i++)
{
stemp = System.Convert.ToString(i, 2);
while(stemp.Length<8) stemp = "0" + stemp;
sTable += "\tconst char nb" + stemp + "=" + i.ToString() + ";\r\n";
}
sTable += "}";
Clipboard.Clear();
Clipboard.SetText ( sTable);
MessageBox.Show(sTable);

Usando esto, para binario de 8 bits, lo uso para hacer una clase estática y la coloca en el portapapeles. Luego se pega en el proyecto y se agrega a la sección Uso, por lo que cualquier cosa con nb001010 se saca de una tabla, en menos estático, pero aún así ... uso C # para una gran cantidad de codificación de gráficos PIC y uso mucho 0b101010 en Hi-Tech C

- Muestra de código outpt--

static class BinaryTable
{   const char nb00000000=0;
    const char nb00000001=1;
    const char nb00000010=2;
    const char nb00000011=3;
    const char nb00000100=4;
//etc, etc, etc, etc, etc, etc, etc, 
}

:-) NEAL

Neal
fuente
3

La característica literal binaria no se implementó en C # 6.0 y Visual Studio 2015. pero el 30 de marzo de 2016 Microsoft anunció la nueva versión de Visual Studio '15' Preview con la que podemos usar literales binarios.

Podemos usar uno o más de los caracteres de subrayado (_) para los separadores de dígitos. para que el fragmento de código se vea así:

int x           = 0b10___10_0__________________00; //binary value of 80
int SeventyFive = 0B100_________1011; //binary value of 75

WriteLine($" {x} \n {SeventyFive}");

y podemos usar 0b y 0B como se muestra en el fragmento de código anterior.

si no desea usar un separador de dígitos, puede usarlo sin un separador de dígitos como el fragmento de código a continuación

int x           = 0b1010000; //binary value of 80
int SeventyFive = 0B1001011; //binary value of 75

WriteLine($" {x} \n {SeventyFive}");
Banketeshvar Narayan
fuente
2

Si bien no es posible usar un Literal, ¿tal vez un BitConverter también puede ser una solución?

Michael Stum
fuente
BitConverter se vuelve un poco complicado porque es machine-endian; y en su Intel estándar, eso significa little-endian. La mayoría de las personas tienen dificultades para leer literales little-endian ...
Marc Gravell
1
Ay, ahora recuerdo por qué siempre fui consciente pero nunca usé el BitConverter ...
Michael Stum
44
BitConverter tiene el campo BitConverter.IsLittleEndian, que puede usar para probar (y revertir un búfer) si la máquina host no es poco endian.
astuto
1

Aunque la solución de análisis de cadenas es la más popular, no me gusta, porque analizar cadenas puede ser un gran éxito en algunas situaciones.

Cuando se necesita un tipo de campo de bits o máscara binaria, prefiero escribirlo como

bitMask largo = 1011001;

Y después

int bit5 = BitField.GetBit (bitMask, 5);

O

bool flag5 = BitField.GetFlag (bitMask, 5); `

Donde está la clase BitField

public static class BitField
{
    public static int GetBit(int bitField, int index)
    {
        return (bitField / (int)Math.Pow(10, index)) % 10;
    }

    public static bool GetFlag(int bitField, int index)
    {
        return GetBit(bitField, index) == 1;
    }
}
Dmitry Tashkinov
fuente
44
Yuck ¿Por qué no solo usar enumeraciones? Usar mil veces para significar 1-0-0-0 es solo pedir problemas.
Clément
1

Puede usar 0b000001desde Visual Studio 2017 (C # 7.0)

Xiaoyuvax
fuente
0

Básicamente, creo que la respuesta es NO, no hay una manera fácil. Use constantes decimales o hexadecimales: son simples y claras. La respuesta de @RoyTinkers también es buena: use un comentario.

int someHexFlag = 0x010; // 000000010000
int someDecFlag = 8;     // 000000001000

Las otras respuestas aquí presentan varias rondas de trabajo útiles, pero creo que no son mejores que la respuesta simple. Los diseñadores de lenguaje C # probablemente consideraron innecesario un prefijo '0b'. HEX es fácil de convertir a binario, y la mayoría de los programadores tendrán que conocer los equivalentes DEC de 0-8 de todos modos.

Además, al examinar los valores en el depurador, se mostrarán como HEX o DEC.

RaoulRubin
fuente