Literais binários em C #

187

Existe uma maneira de escrever literais binários em C #, como prefixar hexadecimal com 0x? 0b não funciona.

Caso contrário, qual é a maneira mais fácil de fazer isso? Algum tipo de conversão de string?

toxvaerd
fonte
2
Use constantes e dê a eles nomes próprios; o fato de você precisar escrevê-los em decimal não deve importar. Pessoalmente, eu gostaria de ler em if (a == MaskForModemIOEnabled)vez deif (a == 0b100101)
Lasse V. Karlsen
2
Outra nota: Se você encontrou este post, porque você quer um campo de bits, por favor considere a sinalizadores de atributo: msdn.microsoft.com/en-us/library/cc138362.aspx#sectionToggle0
toxvaerd
17
Os dois conselhos são muito bons, exceto ao definir os referidos campos de bits, onde essas definições constantes nomeadas podem ser mais fáceis de ler e escrever com um literal binário. [Flags] enum Quantidade {None = 0, One = 1, Some = 0b10, Most = 0b100, Todos = 0b111}
brianary
1
Para o que vale a pena, o suporte a literais binários está planejado para Roslyn .
Joe White
Ainda não é suportado no C # 6.0, certo? Não é possível usar literais binários no vs2015: /
anhoppe

Respostas:

146

O C # 7.0 suporta literais binários (e separadores de dígitos opcionais por meio de caracteres sublinhados).

Um exemplo:

int myValue = 0b0010_0110_0000_0011;

Você também pode encontrar mais informações na página do Roslyn GitHub .

BTownTKD
fonte
3
@ D.Singh Eu vejo isso na lista C # 7 agora. github.com/dotnet/roslyn/issues/2136
Danation
9
Doce! Funcionando muito bem no VS2017 / C # 7.
STLDev
1
É bom lembrar que os literais binários do C # são big-endian, mas em números inteiros x86 são little-endian, portanto Int16 = 0b0010_0110_0000_0011serão armazenados como { 0b0000_0011, 0b0010_0110 }- confusos.
Dai
1
@ Dai que não é diferente de literais hexadecimais. Em outras palavras, todas as constantes são big endian, mas armazenadas pouco endian na Intel / AMD e na maioria dos ARMs. 0xDEADBEEFserá armazenado como0xEF 0xBE 0xAD 0xDE
Cole Johnson
170

Atualizar

O C # 7.0 agora possui literais binários, o que é incrível.

[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
}

Correio Original

Como o tópico parece ter se voltado para declarar valores de sinalizadores baseados em bits em enumerações, pensei que valeria a pena apontar um truque útil para esse tipo de coisa. O operador de deslocamento à esquerda ( <<) permitirá que você pressione um pouco para uma posição binária específica. Combine isso com a capacidade de declarar valores de enum em termos de outros valores da mesma classe e você terá uma sintaxe declarativa muito fácil de ler para enumerações de sinalizadores de bit.

[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
fonte
26
@ ColeJohnson: Muitos desenvolvedores preferem isso. Não sou tão bom em converter hexadecimal na minha cabeça quanto alguns desenvolvedores, e às vezes é melhor atender ao menor denominador comum.
usar o seguinte
2
Eu acho que essa é a maneira mais eficaz, pois as enums são construídas como constantes. Com o atributo [Flags] opcional, eles podem ser usados ​​em operações bit a bit (não diretamente relacionadas à pergunta, mas particularmente úteis ao descrever literais binários). Outra possibilidade interessante é forçar o tipo de enum como um tipo interno (neste exemplo, adicione ': byte' após 'Days'); ver built-in tipos de bit.ly/MKv42E
caligari
Isso é realmente muito fácil de ler, embora declarando a 0 bandeira em um Enum é bastante má prática
miket
5
@ MikeT: Você pode elaborar (ou fornecer um link que elabore) esse último pensamento? Costumo declarar enumerações que começam com "1" porque quero ser capaz de detectar e apresentar falhas quando um valor simplesmente nunca foi inicializado. Mas há algumas situações em que um valor padrão realmente faz sentido, e eu acho que não haveria nada de errado em adicionar um nome para esse valor.
usar o seguinte
3
@MikeT De ca1008 : "Se uma enumeração que tenha o FlagsAttribute aplicado definir um membro com valor zero, seu nome deverá ser 'Nenhum' para indicar que nenhum valor foi definido na enumeração." Portanto, é perfeitamente válido adicioná-lo para servir como valor padrão, se for chamado "Nenhum". No geral, parece-me mais claro se o valor em um campo inicializado por padrão realmente tem um nome para mostrar.
Nyerguds
115

Somente número inteiro e hexadecimal diretamente, receio (ECMA 334v4):

9.4.4.2 Literais inteiros Literais inteiros são usados ​​para escrever valores dos tipos int, uint, long e ulong. Literais inteiros têm duas formas possíveis: decimal e hexadecimal.

Para analisar, você pode usar:

int i = Convert.ToInt32("01101101", 2);
Marc Gravell
fonte
4
Como uma atualização para esta resposta, com as últimas informações modernas de ponta (julho de 2014); O C # 6.0 apresenta literais binários. Veja a minha resposta atualizados wayyyyyy baixo na parte inferior ...
BTownTKD
1
@BTownTKD, sua resposta está realmente no topo :), pois é a resposta aceita.
RBT
25

Adicionando à resposta do @ StriplingWarrior sobre sinalizadores de bits em enumerações, existe uma convenção fácil que você pode usar em hexadecimal para contagem crescente nos turnos de bits. Use a sequência 1-2-4-8, mova uma coluna para a esquerda e 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
}

Digitalize para baixo começando com a coluna da direita e observe o padrão 1-2-4-8 (shift) 1-2-4-8 (shift) ...


Para responder à pergunta original, eu segundo a sugestão de @ Sahuagin de usar literais hexadecimais. Se você estiver trabalhando com números binários com frequência suficiente para que isso seja uma preocupação, vale a pena tentar entender o hexadecimal.

Se você precisar ver números binários no código-fonte, sugiro adicionar comentários com literais binários, como eu tenho acima.

Roy Tinker
fonte
23

Você sempre pode criar quase literais, constantes que contêm o valor que você procura:

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

Se você usá-los frequentemente, poderá agrupá-los em uma classe estática para reutilização.

No entanto, um pouco fora do tópico, se você tiver alguma semântica associada aos bits (conhecida em tempo de compilação), sugiro usar um Enum:

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

Se você observar o status de implementação do recurso de idioma da .NET Compiler Platform ("Roslyn"), poderá ver claramente que no C # 6.0 esse é um recurso planejado; portanto, na próxima versão, podemos fazê-lo da maneira usual.

Status literal binário

totymedli
fonte
3
Isso aconteceu? Nem todos os recursos que foram publicados no blog fizeram o lançamento do Visual Studio 2015.
Coronel Panic
4
@ColonelPanic - Danation indica que está "na lista C # 7 agora" - 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 isso, para binário de 8 bits, eu uso isso para criar uma classe estática e a coloca na área de transferência. Em seguida, ela é colada no projeto e adicionada à seção Usando, para que qualquer coisa com nb001010 seja retirada de uma tabela, em menos estático, mas ainda assim ... eu uso C # para muitos códigos gráficos PIC e uso 0b101010 muito no Hi-Tech C

--sample do 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
fonte
3

O recurso literal binário não foi implementado no C # 6.0 e no Visual Studio 2015. mas, em 30 de março de 2016, a Microsoft anunciou a nova versão do Visual Studio '15' Preview com a qual podemos usar literais binários.

Podemos usar um ou mais de um caractere Sublinhado (_) para separadores de dígitos. então o trecho de código seria algo como:

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

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

e podemos usar 0b e 0B, como mostrado no snippet de código acima.

se você não quiser usar o separador de dígitos, poderá usá-lo sem separador de dígitos, como o trecho de código abaixo

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

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

Embora não seja possível usar um Literal, talvez um BitConverter também possa ser uma solução?

Michael Stum
fonte
O BitConverter fica um pouco complicado porque é endia-máquina; e na sua Intel padrão, isso significa pouco endian. A maioria das pessoas tem dificuldade em ler literais little-endian ...
Marc Gravell
1
Ai, agora eu me lembro por que eu estava sempre ciente, mas nunca usei o BitConverter ...
Michael Stum
4
O BitConverter possui o campo BitConverter.IsLittleEndian, o qual você pode usar para testar (e reverter um buffer) se a máquina host não for muito pequena.
foxy
1

Embora a solução de análise de strings seja a mais popular, não gosto, porque a análise de strings pode ser um ótimo desempenho em algumas situações.

Quando é necessário um tipo de campo de bits ou máscara binária, prefiro escrever como

bitMask longo = 1011001;

E depois

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

Ou

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

Onde a classe 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
fonte
4
Que nojo. Por que não usar enums? Usar ont mil para significar 1-0-0-0 é apenas pedir problemas.
Clément
1

Você pode usar 0b000001desde o Visual Studio 2017 (C # 7.0)

Xiaoyuvax
fonte
0

Basicamente, acho que a resposta é NÃO, não há uma maneira fácil. Use constantes decimais ou hexadecimais - elas são simples e claras. A resposta do @RoyTinkers também é boa - use um comentário.

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

As outras respostas aqui apresentam várias rodadas úteis, mas acho que não são melhores do que a resposta simples. Os designers de linguagem C # provavelmente consideraram um prefixo '0b' desnecessário. O HEX é fácil de converter em binário, e a maioria dos programadores precisará conhecer os equivalentes DEC de 0 a 8 de qualquer maneira.

Além disso, ao examinar valores no depurador, eles serão exibidos com HEX ou DEC.

RaoulRubin
fonte