C # literały binarne


187

Czy istnieje sposób pisania literałów binarnych w języku C #, na przykład przedrostek szesnastkowy z 0x? 0b nie działa.

Jeśli nie, to jak łatwo to zrobić? Jakaś konwersja ciągu?


2
Używaj stałych i nadaj im odpowiednie nazwy, więc fakt, że musisz je zapisywać dziesiętnie, nie powinien mieć znaczenia. Osobiście wolałbym if (a == MaskForModemIOEnabled)raczej czytać niżif (a == 0b100101)
Lasse V. Karlsen

2
Kolejna uwaga: jeśli znalazłeś ten post, ponieważ chcesz mieć trochę pola, rozważ flagi-atrybut: msdn.microsoft.com/en-us/library/cc138362.aspx#sectionToggle0
toxvaerd

17
Obie bardzo dobre porady, z wyjątkiem definiowania wspomnianych pól bitowych, w których te nazwane stałe definicje mogą być łatwiejsze do odczytania i zapisu za pomocą binarnego literału. [Flagi] enum Ilość {Brak = 0, Jeden = 1, Niektóre = 0b10, Większość = 0b100, Wszystko = 0b111}
Brianary

1
Za to, co jest warte, wsparcie dla literałów binarnych jest planowane dla Roslyn .
Joe White

To wciąż nie jest obsługiwane w C # 6.0, prawda? Nie można używać literałów binarnych w vs2015: /
anhoppe

Odpowiedzi:


146

C # 7.0 obsługuje literały binarne (i opcjonalne separatory cyfr za pomocą znaków podkreślenia).

Przykład:

int myValue = 0b0010_0110_0000_0011;

Możesz również znaleźć więcej informacji na stronie Roslyn GitHub .


3
@ D.Singh Widzę to teraz na liście C # 7. github.com/dotnet/roslyn/issues/2136
Danation

9
Słodkie! Świetnie działa w VS2017 / C # 7.
STLDev

1
Pomaga pamiętać, że literały binarne C # są big-endianami, ale na liczbach całkowitych x86 są one little-endian, więc Int16 = 0b0010_0110_0000_0011będą przechowywane jako { 0b0000_0011, 0b0010_0110 }- mylące.
Dai,

1
@Dai nie różni się niczym od liter szesnastkowych. Innymi słowy, wszystkie stałe są dużymi endianami, ale przechowywane endianami na Intel / AMD i większości ARM-ów. 0xDEADBEEFbędą przechowywane jako0xEF 0xBE 0xAD 0xDE
Cole Johnson,

170

Aktualizacja

C # 7.0 ma teraz literały binarne, co jest niesamowite.

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

Oryginalny post

Ponieważ wydaje się, że temat zwrócił się do deklarowania wartości flag opartych na bitach w wyliczeniach, pomyślałem, że warto wskazać przydatną sztuczkę w tego typu sprawach. Operator shift-left ( <<) pozwoli ci przesunąć nieco do określonej pozycji binarnej. W połączeniu z możliwością deklarowania wartości wyliczeniowych w odniesieniu do innych wartości w tej samej klasie, masz bardzo łatwą do odczytania deklaratywną składnię wyliczeń flag bitowych.

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

26
@ColeJohnson: Wielu programistów woli to. Nie jestem tak dobry w przekształcaniu heksów w głowie, jak niektórzy programiści, i czasami najlepiej jest zastosować najniższy wspólny mianownik.
StriplingWarrior

2
Myślę, że jest to najbardziej skuteczny sposób, ponieważ wyliczenia są budowane jako stałe. Dzięki opcjonalnemu atrybutowi [Flagi] można ich używać w operacjach bitowych (niezwiązanych bezpośrednio z pytaniem, ale szczególnie użytecznych przy opisywaniu literałów binarnych). Inną interesującą możliwością jest wymuszenie typu wyliczeniowego jako typu wbudowanego (w tym przykładzie dodaj „: bajt” po „Dniach”); zobacz wbudowane typy w bit.ly/MKv42E
caligari,

W rzeczywistości jest to bardzo łatwe do odczytania, choć zadeklarowanie flagi 0 na Enum jest dość złą praktyką
MikeT

5
@MikeT: Czy możesz rozwinąć (lub podać link, który rozwija) tę ostatnią myśl? Często deklaruję wyliczenia zaczynające się od „1”, ponieważ chcę być w stanie wykryć i szybko zawieść, gdy wartość po prostu nigdy nie została zainicjowana. Ale są sytuacje, w których wartość domyślna ma sens, i sądzę, że dodanie nazwy dla tej wartości nie byłoby niczym złym.
StriplingWarrior

3
@MikeT From ca1008 : „Jeśli wyliczenie z zastosowanym FlagsAttribute definiuje element o wartości zerowej, jego nazwa powinna wynosić„ None ”, aby wskazać, że w wyliczeniu nie zostały ustawione żadne wartości.” Dlatego całkowicie poprawne jest dodanie go, aby służył jako wartość domyślna, jeśli nazywa się „Brak”. Ogólnie rzecz biorąc, wydaje mi się czystsze, jeśli wartość w domyślnie zainicjalizowanym polu faktycznie ma nazwę do wyświetlenia.
Nyerguds,

115

Obawiam się, że tylko liczba całkowita i hex bezpośrednio (ECMA 334v4):

9.4.4.2 Literały całkowite Literały całkowite są używane do zapisywania wartości typów int, uint, long i ulong. Literały całkowite mają dwie możliwe formy: dziesiętną i szesnastkową.

Do parsowania możesz użyć:

int i = Convert.ToInt32("01101101", 2);

4
Jako aktualizacja tej odpowiedzi, z najnowszymi najnowocześniejszymi informacjami (lipiec 2014 r.); C # 6.0 wprowadza literały binarne. Zobacz moją zaktualizowaną odpowiedź Wayyyyyy na dole ...
BTownTKD

1
@BTownTKD twoja odpowiedź jest na górze :), ponieważ jest to odpowiedź zaakceptowana.
RBT

25

Dodając do odpowiedzi @ StriplingWarrior na temat flag bitów w wyliczeniach, istnieje prosta konwencja, której można używać w systemie szesnastkowym do liczenia w górę przez przesunięcia bitów. Użyj sekwencji 1-2-4-8, przesuń jedną kolumnę w lewo i powtórz.

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

Zeskanuj w dół, zaczynając od prawej kolumny i zauważ wzór 1-2-4-8 (shift) 1-2-4-8 (shift) ...


Aby odpowiedzieć na pierwotne pytanie, popieram sugestię @ Sahuagin, by używać literałów szesnastkowych. Jeśli pracujesz z liczbami binarnymi wystarczająco często, aby było to niepokojące, warto poświęcić chwilę na sprawdzenie szesnastkowego.

Jeśli chcesz zobaczyć liczby binarne w kodzie źródłowym, sugeruję dodawanie komentarzy z literałami binarnymi, tak jak mam powyżej.


23

Zawsze możesz tworzyć quasi-literały, stałe zawierające szukaną wartość:

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

Jeśli często ich używasz, możesz owinąć je w klasę statyczną do ponownego użycia.

Jednak nieco nie na temat, jeśli masz jakieś semantyki związane z bitami (znane w czasie kompilacji), sugerowałbym użycie zamiast tego Enum:

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

18

Jeśli spojrzysz na status implementacji funkcji językowych platformy kompilatora .NET („Roslyn”), możesz wyraźnie zauważyć, że w C # 6.0 jest to funkcja planowana, więc w następnej wersji możemy to zrobić w zwykły sposób.

Binarny status dosłowny


3
Czy to się stało? Nie wszystkie funkcje, o których pisano w blogach, faktycznie wydały wersję Visual Studio 2015.
Pułkownik Panic

4
@ColonelPanic - Danation wskazuje, że „znajduje się teraz na liście C # 7” - 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);

Używając tego, dla 8-bitowego pliku binarnego, używam tego, aby utworzyć klasę statyczną i umieszcza go w schowku. Następnie jest wklejany do projektu i dodawany do sekcji Używanie, więc wszystko z nb001010 jest usuwane ze stołu, na najmniej statyczny, ale wciąż ... Używam C # do kodowania grafiki PIC i dużo 0b101010 w Hi-Tech C

- próbka z opisu kodu--

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


3

Funkcja dosłowności binarnej nie została zaimplementowana w C # 6.0 i Visual Studio 2015. ale 30 marca 2016 r. Microsoft ogłosił nową wersję Visual Studio „15” Preview, w której możemy używać literałów binarnych.

Możemy użyć jednego lub więcej niż jednego znaku podkreślenia (_) dla separatorów cyfr. więc fragment kodu wyglądałby mniej więcej tak:

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

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

i możemy użyć wartości 0b i 0B, jak pokazano w powyższym fragmencie kodu.

jeśli nie chcesz używać separatora cyfr, możesz go używać bez separatora cyfr, jak poniżej fragmentu kodu

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

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

2

Chociaż nie jest to możliwe przy użyciu dosłowności , może BitConverter może być również rozwiązaniem?


BitConverter staje się nieco trudny, ponieważ jest endemem maszyny; a na twoim standardowym Intelu oznacza to little-endian. Większość ludzi ma trudności z czytaniem literałów little-endian ...
Marc Gravell

1
Ojej, teraz pamiętam, dlaczego zawsze byłem świadomy, ale nigdy nie korzystałem z BitConvertera ...
Michael Stum

4
BitConverter ma pole BitConverter.IsLittleEndian, którego możesz użyć do przetestowania (i odwrócenia bufora), jeśli komputer-host nie jest małym endianem.
foxy

1

Chociaż parsowanie łańcucha jest najpopularniejsze, nie podoba mi się to, ponieważ parsowanie łańcucha może być świetnym hitem wydajności w niektórych sytuacjach.

Gdy potrzebuję czegoś w rodzaju pola bitowego lub maski binarnej, wolałbym napisać to tak

długa maska ​​bitowa = 1011001;

I później

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

Lub

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

Gdzie jest klasa 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;
    }
}

4
Fuj Dlaczego po prostu nie używać enum? Używanie ont tysiąca w znaczeniu 1-0-0-0 tylko prosi o kłopoty.
Clément


0

Zasadniczo myślę, że odpowiedź brzmi NIE, nie ma łatwego sposobu. Używaj stałych dziesiętnych lub szesnastkowych - są one proste i jasne. Odpowiedź @RoyTinkers jest również dobra - użyj komentarza.

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

Pozostałe odpowiedzi tutaj przedstawiają kilka przydatnych rund - ale myślę, że nie są lepsze niż prosta odpowiedź. Projektanci języka C # prawdopodobnie uznali za niepotrzebny prefiks „0b”. HEX można łatwo przekonwertować na binarny, a większość programistów będzie musiała znać odpowiedniki DEC 0–8.

Ponadto, podczas sprawdzania wartości w debuggerze, będą wyświetlane jako HEX lub DEC.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.