Posso usare un letterale binario in C o C ++?


191

Ho bisogno di lavorare con un numero binario.

Ho provato a scrivere:

const x = 00010000;

Ma non ha funzionato.

So che posso usare un numero esadecimale che ha lo stesso valore di 00010000, ma voglio sapere se esiste un tipo in C ++ per i numeri binari e se non lo è, esiste un'altra soluzione al mio problema?


51
Sai che 00010000è ottale, vero? (E nella tua dichiarazione manca un tipo.)
Keith Thompson,

Qui in modo moderno usando letterali C ++.
Lol4t0

2
C ++ 14 ha aggiunto una funzionalità per questo. Vedi la mia nuova risposta per maggiori dettagli in fondo. Naturalmente, richiede un compilatore che lo implementa.
lpapp

1
@FormlessCloud: queste sono le regole di sintassi fornite negli standard C e C ++ ( 0bappare solo in C ++ 14). Sono progettati per essere inequivocabili.
Keith Thompson,

2
Possibile duplicato dei letterali binari?
MJ Rayburn,

Risposte:


70

Puoi usareBOOST_BINARY mentre aspetti C ++ 0x. :) BOOST_BINARYha probabilmente un vantaggio rispetto all'implementazione del modello in quanto può essere utilizzato anche nei programmi C (è guidato al 100% dal preprocessore).

Per fare il contrario (cioè stampare un numero in forma binaria), è possibile utilizzare il portatile non itoafunzioni , o implementare il proprio .

Sfortunatamente non è possibile eseguire la formattazione di base 2 con flussi STL (poiché setbaserispetterà solo le basi 8, 10 e 16), ma è possibile utilizzare una std::stringversione di itoa(o più concisa, ma marginalmente meno efficiente) std::bitset.

#include <boost/utility/binary.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <bitset>
#include <iostream>
#include <iomanip>

using namespace std;

int main() {
  unsigned short b = BOOST_BINARY( 10010 );
  char buf[sizeof(b)*8+1];
  printf("hex: %04x, dec: %u, oct: %06o, bin: %16s\n", b, b, b, itoa(b, buf, 2));
  cout << setfill('0') <<
    "hex: " << hex << setw(4) << b << ", " <<
    "dec: " << dec << b << ", " <<
    "oct: " << oct << setw(6) << b << ", " <<
    "bin: " << bitset< 16 >(b) << endl;
  return 0;
}

produce:

hex: 0012, dec: 18, oct: 000022, bin:            10010
hex: 0012, dec: 18, oct: 000022, bin: 0000000000010010

Leggi anche The String Formatters of Manor Farm di Herb Sutter per una discussione interessante.


2
Come dice la stessa pagina a cui si collega, è possibile utilizzare solo 8, 10 o 16 con setbase. Tuttavia:int main() { cout << bitset<8>(42); }

@Roger grazie per il bitsetsuggerimento, ho già corretto un po ' setbaseprima di vedere il tuo commento.
Vladr,

Ecco un tutorial sui letterali definiti dall'utente in c ++ 11: akrzemi1.wordpress.com/2012/10/23/user-defined-literals-part-ii . Evidentemente c ++ 1y (aka c ++ 14) includerà letterali binari nello standard.
Cheshirekow,

275

Se si utilizza GCC, è possibile utilizzare un'estensione GCC (inclusa nello standard C ++ 14 ) per questo:

int x = 0b00010000;

2
Diversi altri compilatori hanno questo o altri modi simili per esprimere i numeri nella base 2.
nategoose

4
Sarebbe bello avere questo standardizzato, ma clang supporta la stessa notazione.
polemon

14
Funziona a Clang, GCC e TCC. Non funziona in PCC. Non ho nessun altro compilatore con cui provare.
Michas,

6
Ho visto un numero di compilatori di sistemi embedded che lo supportano. Non conosco alcun motivo particolare per cui non dovrebbe essere una funzione di linguaggio standard.
supercat


98

Puoi usare letterali binari. Sono standardizzati in C ++ 14. Per esempio,

int x = 0b11000;

Supporto in GCC

Il supporto in GCC è iniziato in GCC 4.3 (vedi https://gcc.gnu.org/gcc-4.3/changes.html ) come estensioni della famiglia del linguaggio C (vedi https://gcc.gnu.org/onlinedocs/gcc/ C-Extensions.html # C-Extensions ), ma da GCC 4.9 è ora riconosciuto come una funzionalità C ++ 14 o un'estensione (vedi Differenza tra valori letterali binari GCC e C ++ 14? )

Supporto in Visual Studio

Il supporto in Visual Studio è stato avviato in Visual Studio 2015 Preview (vedere https://www.visualstudio.com/news/vs2015-preview-vs#C++ ).


5
Puoi usare 'per separare ogni parte: "0b0000'0100'0100'0001
camino

1
@camino Bello, puoi perdere il primo "
Nikos,

Questa dovrebbe essere la risposta accettata. La maggior parte delle altre risposte è così superata.
Alex

73
template<unsigned long N>
struct bin {
    enum { value = (N%10)+2*bin<N/10>::value };
} ;

template<>
struct bin<0> {
    enum { value = 0 };
} ;

// ...
    std::cout << bin<1000>::value << '\n';

La cifra più a sinistra del letterale deve essere ancora 1, ma comunque.


4
Versione migliore: bitbucket.org/kniht/scraps/src/tip/cpp/binary.hpp ( binary<10>::value == binary<010>::valuee alcuni controlli degli errori)

In qualche modo ho perso questo prima di pubblicare la mia risposta quasi identica. Ma nel mio la cifra principale deve essere 0, non 1.
Mark Ransom

4
Una versione migliore di questa idea modello: code.google.com/p/cpp-binary-constants
Valentin Galea,

@ValentinGalea - perché la versione di Google è migliore di questa?
AJed

Questo è davvero impressionante. Peccato che non funzioni con un numero elevato di bit.
Il fisico quantistico,

31

Alcuni compilatori (di solito quelli per i microcontrollori ) hanno una funzione speciale implementata nel riconoscimento dei numeri binari letterali con il prefisso "0b ..." che precede il numero, sebbene la maggior parte dei compilatori (standard C / C ++) non abbia tale funzione e se è il caso, ecco la mia soluzione alternativa:

#define B_0000    0
#define B_0001    1
#define B_0010    2
#define B_0011    3
#define B_0100    4
#define B_0101    5
#define B_0110    6
#define B_0111    7
#define B_1000    8
#define B_1001    9
#define B_1010    a
#define B_1011    b
#define B_1100    c
#define B_1101    d
#define B_1110    e
#define B_1111    f

#define _B2H(bits)    B_##bits
#define B2H(bits)    _B2H(bits)
#define _HEX(n)        0x##n
#define HEX(n)        _HEX(n)
#define _CCAT(a,b)    a##b
#define CCAT(a,b)   _CCAT(a,b)

#define BYTE(a,b)        HEX( CCAT(B2H(a),B2H(b)) )
#define WORD(a,b,c,d)    HEX( CCAT(CCAT(B2H(a),B2H(b)),CCAT(B2H(c),B2H(d))) )
#define DWORD(a,b,c,d,e,f,g,h)    HEX( CCAT(CCAT(CCAT(B2H(a),B2H(b)),CCAT(B2H(c),B2H(d))),CCAT(CCAT(B2H(e),B2H(f)),CCAT(B2H(g),B2H(h)))) )

// Using example
char b = BYTE(0100,0001); // Equivalent to b = 65; or b = 'A'; or b = 0x41;
unsigned int w = WORD(1101,1111,0100,0011); // Equivalent to w = 57155; or w = 0xdf43;
unsigned long int dw = DWORD(1101,1111,0100,0011,1111,1101,0010,1000); //Equivalent to dw = 3745774888; or dw = 0xdf43fd28;

Svantaggi (non è così grandi):

  • I numeri binari devono essere raggruppati 4 per 4;
  • I letterali binari devono essere solo numeri interi senza segno;

Vantaggi :

  • Totale preprocessore guidato, non spending processor timein operazioni inutili ( like "?.. :..", "<<", "+") al programma eseguibile (può essere eseguito centinaia di volte nell'applicazione finale);
  • Funziona anche con "mainly in C"compilatori e C ++ ( template+enum solution works only in C++ compilers);
  • Ha solo il limite di "longness" per esprimere valori di "costante letterale". Ci sarebbe stata una limitazione di longitudine iniziale (di solito 8 bit: 0-255) se uno avesse espresso valori costanti analizzando la risoluzione di "enum solution" (usually 255 = reach enum definition limit), diversamente, i limiti di "costante letterale", nel compilatore consente numeri maggiori;
  • Alcune altre soluzioni richiedono un numero esagerato di definizioni costanti (troppe definizioni secondo me) comprese lunghe o several header files(nella maggior parte dei casi non facilmente leggibili e comprensibili, e rendono il progetto inutilmente confuso ed esteso, come quello che utilizza "BOOST_BINARY()");
  • Semplicità della soluzione: facilmente leggibile, comprensibile e regolabile per altri casi (potrebbe essere esteso anche per il raggruppamento 8 per 8);

Perché ad esempio B_0100non viene utilizzato (anziché 0100)? Come ad es char b = BYTE(0100,0001);.
Peter Mortensen,

@PeterMortensen Il B_ viene aggiunto dalla _B2Hfunzione preprocessore.
mxmlnkn,

20

Questo thread può essere d'aiuto.

/* Helper macros */
#define HEX__(n) 0x##n##LU
#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 */
#define B8(d) ((unsigned char)B8__(HEX__(d)))
#define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) \
+ B8(dlsb))
#define B32(dmsb,db2,db3,dlsb) (((unsigned long)B8(dmsb)<<24) \
+ ((unsigned long)B8(db2)<<16) \
+ ((unsigned long)B8(db3)<<8) \
+ B8(dlsb))


#include <stdio.h>

int main(void)
{
    // 261, evaluated at compile-time
    unsigned const number = B16(00000001,00000101);

    printf("%d \n", number);
    return 0;
}

Funziona! (Tutti i crediti vanno a Tom Torfs.)


non ho davvero capito (ima principiante nella programmazione e specialmente in C ++) ma sembra interessante quindi cercherò di capirlo dopo alcuni altri studi C ++, grazie
hamza

3
La macro B8 funziona convertendo il letterale "binario" in letterale esadecimale ed estraendo ogni 4 bit.
dan04

Mi chiedo cosa significhi 0x ## n ## LU? Mai riscontrato tale sintassi.
Federico A. Ramponi,

@hamza: è davvero piuttosto intricato. Ma quello che devi capire è solo da #include <stdio> in poi.
Federico A. Ramponi,

8
@Federico: l' ##operatore del preprocessore incolla i token insieme. Quindi, in questo caso, se chiami HEX__(10), si espande a 0x10LU.
James McNellis,

18

Come già risposto, gli standard C non hanno modo di scrivere direttamente numeri binari. Esistono tuttavia estensioni del compilatore e apparentemente C ++ 14 include il 0bprefisso binario. (Nota che questa risposta è stata originariamente pubblicata nel 2010.)

Una soluzione popolare è includere un file di intestazione con macro di supporto . Un'opzione semplice è anche quella di generare un file che includa definizioni macro per tutti i pattern a 8 bit, ad esempio:

#define B00000000 0
#define B00000001 1
#define B00000010 2

Ciò si traduce in soli 256 #definesecondi e, se sono necessarie costanti binarie superiori a 8 bit, queste definizioni possono essere combinate con turni e OR, possibilmente con macro helper (ad es BIN16(B00000001,B00001010).). (Avere macro individuali per ogni 16 bit, per non parlare di 32 bit, il valore non è plausibile.)

Naturalmente il rovescio della medaglia è che questa sintassi richiede la scrittura di tutti gli zeri iniziali, ma ciò potrebbe anche rendere più chiaro l'uso come flag di bit e contenuti dei registri hardware. Per una macro simile a una funzione che risulta in una sintassi senza questa proprietà, vedere il bithacks.hcollegamento sopra.


2
Quindi, quanto è grande un file che il CPP dovrebbe leggere se avessi tutte le macro per un long long int?
Wilhelmtell,

3
@wilhelmtell: E qual è la rilevanza di ciò quando ho specificato "tutti i pattern a 8 bit " (= 256 righe) e ho suggerito di combinare quantità maggiori da quelle? Anche il BOOST_BINARY della risposta accettata definisce tutti i pattern a 8 bit nell'intestazione ...
Arkku

16

La mentalità di over-engineering del C ++ è già ben spiegata nelle altre risposte qui. Ecco il mio tentativo di farlo con una mentalità C, keep-it-simple-ffs:

unsigned char x = 0xF; // binary: 00001111

12

C non ha notazione nativa per numeri binari puri. La tua scommessa migliore qui sarebbe o ottale (ad esempio 07777) di esadecimale (ad esempio 0xfff).


11

È possibile utilizzare la funzione presente in questa domanda per ottenere fino a 22 bit in C ++. Ecco il codice dal link, opportunamente modificato:

template< unsigned long long N >
struct binary
{
  enum { value = (N % 8) + 2 * binary< N / 8 > :: value } ;
};

template<>
struct binary< 0 >
{
  enum { value = 0 } ;
};

Quindi puoi fare qualcosa del genere binary<0101011011>::value.


7

L'unità più piccola con cui puoi lavorare è un byte (che è di chartipo). Puoi lavorare con i bit usando operatori bit per bit.

Per quanto riguarda i letterali interi, puoi lavorare solo con numeri decimali (base 10), ottali (base 8) o esadecimali (base 16). Non ci sono letterali binari (base 2) in C né C ++.

I numeri ottali hanno il prefisso 0e i numeri esadecimali sono preceduti da 0x. I numeri decimali non hanno prefisso.

In C ++ 0x sarai in grado di fare ciò che vuoi tra l'altro tramite valori letterali definiti dall'utente .


posso almeno mostrare il valore binario di un esadecimale in una funzione di stampa o cout?
Hamza,

Sì, è possibile <shameless_plug> stackoverflow.com/questions/2611764#2611883 </shameless_plug>
vladr

5
Alcuni compilatori C supportano 0b100101 per valori letterali binari, ma sfortunatamente si tratta di un'estensione non standard.
Joey Adams,

3
Si noti che, sebbene non sia definito nello standard, alcuni compilatori (in particolare quelli per microcontrollori e sistemi integrati) aggiungono la sintassi per il binario nel modulo 0b00101010per comodità. SDCC è uno e sono sicuro che ce ne sono anche altri. (Modifica: Ah, battimi, @Joey!)
Matt B.

5

È inoltre possibile utilizzare l'assemblaggio inline in questo modo:

int i;

__asm {
    mov eax, 00000000000000000000000000000000b
    mov i,   eax
}

std::cout << i;

Va bene, potrebbe essere un po 'eccessivo, ma funziona.


3
La tua soluzione non è multipiattaforma. In molte architetture non è possibile includere il codice assembly in C. In particolare nel compilatore di Microsoft Visual Studio è possibile (quando compilato per x86 32 bit). Ma come fai a sapere se il tuo processore ha il registro "eax"? Pensa ai processori ARM nei telefoni cellulari, nei processori x64, ecc. Non hanno "eax". Il processore MIPS non ha nemmeno il comando 'mov'
DanielHsH,

4

Basato su alcune altre risposte, ma questo rifiuterà i programmi con valori letterali binari illegali. Gli zeri iniziali sono opzionali.

template<bool> struct BinaryLiteralDigit;

template<> struct BinaryLiteralDigit<true> {
    static bool const value = true;
};

template<unsigned long long int OCT, unsigned long long int HEX>
struct BinaryLiteral {
    enum {
        value = (BinaryLiteralDigit<(OCT%8 < 2)>::value && BinaryLiteralDigit<(HEX >= 0)>::value
            ? (OCT%8) + (BinaryLiteral<OCT/8, 0>::value << 1)
            : -1)
    };
};

template<>
struct BinaryLiteral<0, 0> {
    enum {
        value = 0
    };
};

#define BINARY_LITERAL(n) BinaryLiteral<0##n##LU, 0x##n##LU>::value

Esempio:

#define B BINARY_LITERAL

#define COMPILE_ERRORS 0

int main (int argc, char ** argv) {
    int _0s[] = { 0, B(0), B(00), B(000) };
    int _1s[] = { 1, B(1), B(01), B(001) };
    int _2s[] = { 2, B(10), B(010), B(0010) };
    int _3s[] = { 3, B(11), B(011), B(0011) };
    int _4s[] = { 4, B(100), B(0100), B(00100) };

    int neg8s[] = { -8, -B(1000) };

#if COMPILE_ERRORS
    int errors[] = { B(-1), B(2), B(9), B(1234567) };
#endif

    return 0;
}

3

Il "tipo" di un numero binario è uguale a qualsiasi numero decimale, esadecimale o ottale: int(o anche carattere, corto, lungo lungo).

Quando assegni una costante, non puoi assegnarla con 11011011 (curiosamente e sfortunatamente), ma puoi usare hex. Hex è un po 'più facile da tradurre mentalmente. Chunk in nibbles (4 bit) e traduci in un carattere in [0-9a-f].


2

Puoi usare un bitset

bitset<8> b(string("00010000"));
int i = (int)(bs.to_ulong());
cout<<i;

2

Ho esteso la buona risposta data da @ renato-chandelier garantendo il supporto di:

  • _NIBBLE_(…) - 4 bit, 1 nibble come argomento
  • _BYTE_(…) - 8 bit, 2 nibble come argomenti
  • _SLAB_(…) - 12 bit, 3 stuzzichini come argomenti
  • _WORD_(…) - 16 bit, 4 stuzzichini come argomenti
  • _QUINTIBBLE_(…) - 20 bit, 5 stuzzichini come argomenti
  • _DSLAB_(…) - 24 bit, 6 stuzzichini come argomenti
  • _SEPTIBBLE_(…) - 28 bit, 7 stuzzichini come argomenti
  • _DWORD_(…) - 32 bit, 8 nibble come argomenti

In realtà non sono così sicuro dei termini "quintibble" e "septibble". Se qualcuno conosce qualche alternativa, per favore fatemelo sapere.

Ecco la macro riscritta:

#define __CAT__(A, B) A##B
#define _CAT_(A, B) __CAT__(A, B)

#define __HEX_0000 0
#define __HEX_0001 1
#define __HEX_0010 2
#define __HEX_0011 3
#define __HEX_0100 4
#define __HEX_0101 5
#define __HEX_0110 6
#define __HEX_0111 7
#define __HEX_1000 8
#define __HEX_1001 9
#define __HEX_1010 a
#define __HEX_1011 b
#define __HEX_1100 c
#define __HEX_1101 d
#define __HEX_1110 e
#define __HEX_1111 f

#define _NIBBLE_(N1) _CAT_(0x, _CAT_(__HEX_, N1))
#define _BYTE_(N1, N2) _CAT_(_NIBBLE_(N1), _CAT_(__HEX_, N2))
#define _SLAB_(N1, N2, N3) _CAT_(_BYTE_(N1, N2), _CAT_(__HEX_, N3))
#define _WORD_(N1, N2, N3, N4) _CAT_(_SLAB_(N1, N2, N3), _CAT_(__HEX_, N4))
#define _QUINTIBBLE_(N1, N2, N3, N4, N5) _CAT_(_WORD_(N1, N2, N3, N4), _CAT_(__HEX_, N5))
#define _DSLAB_(N1, N2, N3, N4, N5, N6) _CAT_(_QUINTIBBLE_(N1, N2, N3, N4, N5), _CAT_(__HEX_, N6))
#define _SEPTIBBLE_(N1, N2, N3, N4, N5, N6, N7) _CAT_(_DSLAB_(N1, N2, N3, N4, N5, N6), _CAT_(__HEX_, N7))
#define _DWORD_(N1, N2, N3, N4, N5, N6, N7, N8) _CAT_(_SEPTIBBLE_(N1, N2, N3, N4, N5, N6, N7), _CAT_(__HEX_, N8))

Ed ecco l'esempio usando Renato:

char b = _BYTE_(0100, 0001); /* equivalent to b = 65; or b = 'A'; or b = 0x41; */
unsigned int w = _WORD_(1101, 1111, 0100, 0011); /* equivalent to w = 57155; or w = 0xdf43; */
unsigned long int dw = _DWORD_(1101, 1111, 0100, 0011, 1111, 1101, 0010, 1000); /* Equivalent to dw = 3745774888; or dw = 0xdf43fd28; */

0

Basta usare la libreria standard in C ++:

#include <bitset>

Hai bisogno di una variabile di tipo std::bitset:

std::bitset<8ul> x;
x = std::bitset<8>(10);
for (int i = x.size() - 1; i >= 0; i--) {
      std::cout << x[i];
}

In questo esempio, ho memorizzato la forma binaria di 10in x.

8uldefinisce la dimensione dei bit, quindi 7ulsignifica sette bit e così via.



Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.