Чи можу я використовувати двійковий літерал на C або C ++?


191

Мені потрібно працювати з двійковим номером.

Я спробував написати:

const x = 00010000;

Але це не спрацювало.

Я знаю, що я можу використовувати шістнадцяткове число, яке має те саме значення, що й значення 00010000, але я хочу знати, чи є тип C ++ для двійкових чисел, а якщо немає, чи є інше рішення для моєї проблеми?


51
Ви знаєте, що 00010000вісімковий, так? (І у вашій декларації відсутній тип.)
Кіт Томпсон,

Ось сучасний спосіб використання літератур C ++.
Lol4t0

2
C ++ 14 додав для цього функцію. Дивіться мою нову відповідь для більш детальної інформації внизу. Звичайно, для цього потрібен компілятор, який реалізує його.
lpapp

1
@FormlessCloud: Це правила синтаксису, наведені у стандартах C і C ++ ( 0bз'являється лише у C ++ 14). Вони розроблені так, щоб бути однозначними.
Кіт Томпсон

2
Можливий дублікат двійкових літералів?
MJ Rayburn

Відповіді:


70

Ви можете використовуватиBOOST_BINARY під час очікування C ++ 0x. :) BOOST_BINARYМожливо, має перевагу перед реалізацією шаблону, наскільки він може бути використаний і в програмах C (це 100%, керований препроцесором.)

Щоб зробити зворотне (тобто роздрукувати число у двійковій формі), ви можете скористатися itoaфункцією , що не переноситься , або здійснити власну .

На жаль, ви не можете виконати форматування базового 2 за допомогою потоків STL (оскільки setbaseбудуть вшановуватись лише бази 8, 10 і 16), але ви можете використовувати або std::stringверсію itoa, або (тим більш стислий, але незначно менш ефективний) 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;
}

виробляє:

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

Читайте також "Струнні формати фортеці Садиби" Герба Саттера для цікавої дискусії.


2
Як говорить сама сторінка, на яку ви посилаєтеся, ви можете використовувати лише 8, 10 або 16 із встановленою базою даних. Однак:int main() { cout << bitset<8>(42); }

@Roger спасибі за bitsetпідказку, я вже виправив трохи про те, setbaseперш ніж я побачив ваш коментар.
vladr

Ось підручник з визначеними користувачем літералами c ++ 11: akrzemi1.wordpress.com/2012/10/23/user-defined-literals-part-ii . Очевидно, що c ++ 1y (він же c ++ 14) включатиме в стандарт двійкові літерали.
cheshirekow

275

Якщо ви використовуєте GCC, ви можете використовувати для цього розширення GCC (яке включено до стандарту C ++ 14 ):

int x = 0b00010000;

2
Кілька інших компіляторів мають цей або інші подібні способи вираження чисел у базі 2.
nategoose

4
Було б добре, щоб це було стандартизовано, але кланг підтримує ті ж позначення.
полемон

14
Він працює в Clang, GCC та TCC. Це не працює в PCC. У мене немає іншого компілятора, з яким можна тестувати.
Michas

6
Я бачив ряд компіляторів вбудованих систем, які підтримують його. Я не знаю жодної конкретної причини, щоб вона не була стандартною мовою.
supercat


98

Можна використовувати бінарні літерали. Вони стандартизовані в C ++ 14. Наприклад,

int x = 0b11000;

Підтримка в GCC

Підтримка в GCC почалася в GCC 4.3 (див. Https://gcc.gnu.org/gcc-4.3/changes.html ) як розширення до сімейства мов C (див. Https://gcc.gnu.org/onlinedocs/gcc/ C-Extensions.html # C-Extensions ), але оскільки GCC 4.9 тепер він визнається або функцією C ++ 14, або розширенням (див. Різниця між бінарними літералами GCC та C ++ 14? )

Підтримка у Visual Studio

Підтримка в Visual Studio розпочалася в Preview Studio Visual Studio 2015 (див. Https://www.visualstudio.com/news/vs2015-preview-vs#C++ ).


5
Ви можете використовувати ", щоб розділити кожну частину:" 0b0000'0100'0100'0001
camino

1
@camino Приємно, що ти можеш втратити перший "
Нікос

Це має бути прийнятою відповіддю. Більшість інших відповідей застаріли.
Олексій

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';

Найменша ліва цифра буквально все ж повинна бути 1, але тим не менше.


4
Краща версія: bitbucket.org/kniht/scraps/src/tip/cpp/binary.hpp ( binary<10>::value == binary<010>::valueі деяка перевірка помилок)

Якось пропустив цю, перш ніж я опублікував власну майже однакову відповідь. Але в шахті провідна цифра повинна бути 0, а не 1.
Марк Ransom

4
Краща версія цієї ідеї шаблону: code.google.com/p/cpp-binary-constants
Валентин Галея

@ValentinGalea - чому версія google краща за цю?
AJed

Це вигадливо вражає. Шкода, що він не працює для великої кількості біт.
Квантовий фізик

31

Кілька компіляторів (як правило, для мікроконтролерів ) мають особливу функцію, реалізовану в рамках розпізнавання буквальних двійкових чисел за допомогою префіксу "0b ...", що передує номеру, хоча більшість компіляторів (стандарти C / C ++) не мають такої функції, і якщо вона так, ось це моє альтернативне рішення:

#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;

Недоліки (це не такі великі):

  • Двійкові числа повинні бути згруповані 4 на 4;
  • Бінарні літерали повинні бути лише непідписаними цілими числами;

Переваги :

  • Загальний запуск препроцесора, а не spending processor timeбезглуздих операцій (like "?.. :..", "<<", "+" ) з виконуваною програмою (він може виконуватися сто разів у остаточному застосуванні);
  • Він також працює "mainly in C"компіляторами та C ++ (template+enum solution works only in C++ compilers );
  • Він має лише обмеження "довготи" для вираження значень "буквальна константа". Було б раннє обмеження довготи (як правило, 8 біт: 0-255), якби хтось виражав постійні значення, розбираючи роздільну здатність"enum solution" (usually 255 = reach enum definition limit) "буквальної постійної", у компіляторі допускається більша кількість;
  • Деякі інші рішення вимагають завищеної кількості постійних визначень (занадто багато визначає на мій погляд), включаючи довгі або several header files(в більшості випадків не легко читаються та зрозумілі), і змушують проект непотрібно плутати і розширювати, як, наприклад, використання"BOOST_BINARY()" );
  • Простота рішення: легко читається, зрозуміла та регульована для інших випадків (може бути розширена для групування 8 на 8);

Чому напр. B_0100Не використовується (замість 0100)? Як, наприклад , в char b = BYTE(0100,0001);.
Пітер Мортенсен

@PeterMortensen B_ додається функцією _B2Hпрепроцесора.
mxmlnkn

20

Ця нитка може допомогти.

/* 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;
}

Це працює! (Усі кредити належать Тому Торфсу.)


я не дуже розумів (імає новачок у програмуванні та спеціально на C ++), але це здається цікавим, тому я спробую зрозуміти це після ще декількох досліджень на C ++, дякую
hamza

3
Макрос B8 працює, перетворюючи "бінарний" літерал в шестнадцятковий буквальний і витягуючи кожен 4-й біт.
dan04

Цікаво, що означає 0x ## n ## LU? Ніколи не стикався з таким синтаксисом.
Федеріко А. Рампоні

@hamza: це справді досить складно. Але те, що вам потрібно зрозуміти - це лише від #include <stdio> і далі.
Федеріко А. Рампоні

8
@Federico: Оператор ##препроцесора вставляє лексеми разом. Отже, у цьому випадку, якщо ви телефонуєте HEX__(10), він розширюється до 0x10LU.
Джеймс Мак-Нілліс

18

Як уже було сказано, стандарти С не мають можливості безпосередньо записувати двійкові числа. Однак є розширення компілятора, і, мабуть, C ++ 14 включає 0bпрефікс для двійкових. (Зауважте, що ця відповідь була опублікована в 2010 році.)

Одне популярне рішення - включити заголовок з допоміжними макросами . Одним з простих варіантів є також генерування файлу, який включає визначення макросу для всіх 8-бітних шаблонів, наприклад:

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

Це призводить до лише 256 #define с, і якщо потрібно більше 8-бітових бінарних констант, ці визначення можуть поєднуватися зі зрушеннями та АБО, можливо, з помічниками макросів (наприклад,BIN16(B00000001,B00001010) ). (Маючи окремі макроси для кожні 16-бітові, не кажучи вже про 32-бітові, значення не є правдоподібним.)

Звичайно, недоліком є ​​те, що цей синтаксис вимагає записувати всі провідні нулі, але це також може зробити його більш зрозумілим для таких застосувань, як встановлення бітових прапорів та вмісту апаратних регістрів. Про функціональний макрос, що призводить до синтаксису без цієї властивості, див. bithacks.hПов'язане вище.


2
Отже, наскільки великий файл потрібно було б прочитати CPP, якби у вас були всі макроси для long long int?
wilhelmtell

3
@wilhelmtell: І в чому полягає актуальність цього, коли я вказав "всі 8-бітні шаблони" (= 256 рядків) і запропонував поєднувати великі кількості з цими? Навіть BOOST_BINARY прийнятої відповіді визначає всі 8-бітні шаблони в заголовку…
Arkku

16

Інші інженерні нагляди C ++ вже добре описані в інших відповідях тут. Ось моя спроба зробити це за допомогою C, mind-it-simple-ffs mindset:

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

12

C не має власних позначень для чистих двійкових чисел. Ваша найкраща ставка тут буде або вісімкової (наприклад 07777) шістнадцяткової (наприклад 0xfff).


11

Ви можете скористатися функцією, знайденою в цьому питанні, щоб отримати до 22 біт на C ++. Ось код за посиланням, відповідним чином відредагований:

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

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

Так ви можете зробити щось на кшталт binary<0101011011>::value.


7

Найменша одиниця, з якою можна працювати, - це байт ( charтип якого). Однак ви можете працювати з бітами, використовуючи побітові оператори.

Що стосується цілих літералів, ви можете працювати лише з десятковими (база 10), восьмикутними (основа 8) або шістнадцяткові (база 16) чисел. У C, ні C ++ немає бінарних (база 2) літералів.

Восьмі числа з префіксом, 0а шістнадцяткові числа - з префіксом 0x. Десяткові числа не мають префікса.

У C ++ 0x ви зможете, до речі, робити все, що хочете, за допомогою визначених користувачем літералів .


чи можу я принаймні показати двійкове значення шістнадцяткової у функції друку чи cout?
хамза

Так , ви можете <shameless_plug> stackoverflow.com/questions/2611764#2611883 </shameless_plug>
vladr

5
Деякі компілятори C підтримують 0b100101 для бінарних літералів, але, на жаль, це нестандартне розширення.
Джої Адамс

3
Зауважте, що, хоча це не визначено у стандарті, деякі компілятори (зокрема, мікроконтролери та вбудовані системи) додають синтаксис для двійкового у вигляді 0b00101010зручності. SDCC - це один, і я впевнений, що є й інші. (Редагувати: Га, побий мене, @Joey!)
Метт Б.

5

Ви також можете використовувати вбудовану збірку так:

int i;

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

std::cout << i;

Гаразд, це може бути дещо зайвим, але це працює.


3
Ваше рішення не є багатоплатформеним. У багатьох архітектурах ви не можете включити код складання в C. Спеціально в компіляторі Microsoft Visual studio ви можете (коли компілюється для x86 32bit). Але як ви навіть знаєте, чи має ваш процесор реєстрацію «eax»? Подумайте про процесори ARM у мобільних телефонах, процесорі x64 тощо. У них немає "eax". MIPS-процесор навіть не має команди 'mov'
DanielHsH

4

На основі деяких інших відповідей, але ця буде відхиляти програми з незаконними бінарними літералами. Провідні нулі необов’язкові.

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

Приклад:

#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

"Тип" двійкового числа такий самий, як і будь-яке десяткове, шестинадцяткове або восьмеричне число: int(або навіть знакові, короткі, довгі довгі).

Якщо ви призначаєте константу, ви не можете призначити її за допомогою 11011011 (цікаво і на жаль), але ви можете використовувати шістнадцятковий. Hex трохи легше подумки перекласти. Збийте куски (4 біти) і перекладіть на символ у [0-9a-f].


2

Можна використовувати біт

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

2

Я поширив добру відповідь, надану @ renato-chandelier, забезпечивши підтримку:

  • _NIBBLE_(…) - 4 біти, 1 аргумент як аргумент
  • _BYTE_(…) - 8 біт, 2 мітли як аргументи
  • _SLAB_(…) - 12 біт, 3 мітли як аргументи
  • _WORD_(…) - 16 біт, 4 грибки як аргументи
  • _QUINTIBBLE_(…) - 20 біт, 5 грибів як аргументів
  • _DSLAB_(…) - 24 біти, 6 грибів як аргументів
  • _SEPTIBBLE_(…) - 28 біт, 7 ніблів як аргументів
  • _DWORD_(…) - 32 біти, 8 ніблів в якості аргументів

Я насправді не настільки впевнений у термінах "quintibble" та "septibble". Якщо хтось знає якусь альтернативу, будь ласка, дайте мені знати.

Ось макрос переписаний:

#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))

І ось приклад Ренато на прикладі:

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

Просто використовуйте стандартну бібліотеку на C ++:

#include <bitset>

Вам потрібна змінна тип std::bitset:

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

У цьому прикладі я зберігав двійкову форму 10в x.

8ulвизначає розмір ваших бітів, 7ulзначить, сім біт і так далі.


-1

C ++ надає стандартний шаблон з назвою std::bitset. Спробуйте, якщо вам подобається.


-9

Ви можете спробувати скористатися масивом bool:

bool i[8] = {0,0,1,1,0,1,0,1}

2
Багато подій, жодних пояснень. Ось ваше пояснення: stackoverflow.com/questions/2064550/c-why-bool-is-8-bits-long Також кожен елемент у масиві знаходиться на іншій пам'яті. Але ми хочемо, щоб послідовність упакованих номерів 1 і 0 за однією адресою.
JMI MADISON
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.