Бинарные литералы?

В коде я иногда вижу, что люди задают константы в шестнадцатеричном формате следующим образом:

const int has_nukes        = 0x0001;
const int has_bio_weapons  = 0x0002;
const int has_chem_weapons = 0x0004;
// ...
int arsenal = has_nukes | has_bio_weapons | has_chem_weapons; // all of them
if(arsenal &= has_bio_weapons){
  std::cout << "BIO!!"
}

Но мне не имеет смысла использовать здесь шестнадцатеричный формат. Есть ли способ сделать это прямо в двоичном формате? Что-то вроде этого:

const int has_nukes        = 0b00000000000000000000000000000001;
const int has_bio_weapons  = 0b00000000000000000000000000000010;
const int has_chem_weapons = 0b00000000000000000000000000000100;
// ...

Я знаю, что компиляторы C/С++ не будут компилировать это, но должно быть обходное решение? Возможно ли это на других языках, таких как Java?

Ответ 1

Я бы использовал оператор смены битов:

const int has_nukes        = 1<<0;
const int has_bio_weapons  = 1<<1;
const int has_chem_weapons = 1<<2;
// ...
int dangerous_mask = has_nukes | has_bio_weapons | has_chem_weapons;
bool is_dangerous = (country->flags & dangerous_mask) == dangerous_mask;

Это даже лучше, чем поток 0.

Ответ 2

В С++ 14 вы сможете использовать бинарные литералы со следующим синтаксисом:

0b010101010 /* more zeros and ones */

Эта функция уже реализована в последних clang и gcc. Вы можете попробовать, если вы запустите эти компиляторы с опцией -std=c++1y.

Ответ 3

Кстати, следующая версия С++ будет поддерживать определенные пользователем литералы. Они уже включены в рабочий проект. Это позволяет использовать этот материал (давайте надеяться, что в нем не так много ошибок):

template<char... digits>
constexpr int operator "" _b() {
    return conv2bin<digits...>::value;
}

int main() {
    int const v = 110110110_b;
}

conv2bin будет таким шаблоном:

template<char... digits>
struct conv2bin;

template<char high, char... digits>
struct conv2bin<high, digits...> {
    static_assert(high == '0' || high == '1', "no bin num!");
    static int const value = (high - '0') * (1 << sizeof...(digits)) + 
                             conv2bin<digits...>::value;
};

template<char high>
struct conv2bin<high> {
    static_assert(high == '0' || high == '1', "no bin num!");
    static int const value = (high - '0');
};

Ну, мы получаем двоичные литералы, которые полностью оцениваются во время компиляции из-за "constexpr" выше. Вышеприведенный тип использует тип жесткого кодирования int. Я думаю, что даже это может зависеть от длины двоичной строки. Он использует следующие функции для всех, кто интересуется:

На самом деле, текущая магистраль GCC уже реализует вариативные шаблоны и статические утверждения. Будем надеяться, что он скоро поддержит двух других. Я думаю, что С++ 1x будет качать дом.

Ответ 4

Стандартная библиотека С++ - ваш друг:

#include <bitset>

const std::bitset <32> has_nukes( "00000000000000000000000000000001" );

Ответ 5

Вы можете использовать < < если хотите.

int hasNukes = 1;
int hasBioWeapons = 1 << 1;
int hasChemWeapons = 1 << 2;

Ответ 7

Это обсуждение может быть интересным... Возможно, поскольку ссылка мертва, к сожалению. В нем описывался подход, основанный на шаблонах, похожий на другие ответы здесь.

А также есть вещь, называемая BOOST_BINARY.

Ответ 8

Термин, который вы хотите, это бинарные литералы

Ruby имеет их с синтаксисом, который вы даете.

Один из вариантов заключается в определении вспомогательных макросов для преобразования для вас. Я нашел следующий код в http://bytes.com/groups/c/219656-literal-binary

/* Binary constant generator macro
By Tom Torfs - donated to the public domain
*/

/* All macro evaluate to compile-time constants */

/* *** helper macros *** /

/* turn a numeric literal into a hex constant
(avoids problems with leading zeroes)
8-bit constants max value 0x11111111, always fits in unsigned long
*/
#define HEX__(n) 0x##n##LU

/* 8-bit conversion function */
#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 *** /

/* for upto 8-bit binary constants */
#define B8(d) ((unsigned char)B8__(HEX__(d)))

/* for upto 16-bit binary constants, MSB first */
#define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) \
+ B8(dlsb))

/* for upto 32-bit binary constants, MSB first */
#define B32(dmsb,db2,db3,dlsb) (((unsigned long)B8(dmsb)<<24) \
+ ((unsigned long)B8(db2)<<16) \
+ ((unsigned long)B8(db3)<<8) \
+ B8(dlsb))

/* Sample usage:
B8(01010101) = 85
B16(10101010,01010101) = 43605
B32(10000000,11111111,10101010,01010101) = 2164238933
*/

Ответ 9

Следующая версия С++, С++ 0x представит пользовательские литералы. Я не уверен, что двоичные числа будут частью стандарта, но в худшем случае вы сможете включить его самостоятельно:

int operator "" _B(int i);

assert( 1010_B == 10);

Ответ 10

Я пишу бинарные литералы вроде этого:

const int has_nukes        = 0x0001;
const int has_bio_weapons  = 0x0002;
const int has_chem_weapons = 0x0004;

Он более компактный, чем ваши предполагаемые обозначения, и его легче читать. Например:

const int upper_bit = 0b0001000000000000000;

против

const int upper_bit = 0x04000;

Вы заметили, что двоичная версия не была даже кратной 4 битам? Вы считали, что это 0x10000?

С небольшой практикой гекс или восьмеричный легче для человека, чем для двоичного. И, на мой взгляд, легче читать это, используя операторы сдвига. Но я соглашусь, что мои годы работы на ассемблере могут уклоняться от меня в этот момент.

Ответ 11

Java также не поддерживает бинарные литералы, к сожалению. Однако он имеет enums, который можно использовать с EnumSet. EnumSet представляет значения enum внутри с битовыми полями и представляет Set интерфейс для управления этими флагами.

В качестве альтернативы вы можете использовать битовые смещения (в десятичной форме) при определении своих значений:

const int HAS_NUKES        = 0x1 << 0;
const int HAS_BIO_WEAPONS  = 0x1 << 1;
const int HAS_CHEM_WEAPONS = 0x1 << 2;

Ответ 12

Я согласен с тем, что полезно иметь вариант для бинарных литералов, и они присутствуют на многих языках программирования. В C я решил использовать макрос следующим образом:

#define bitseq(a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, \
           a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) \
   (a31|a30<< 1|a29<< 2|a28<< 3|a27<< 4|a26<< 5|a25<< 6|a24<< 7| \
a23<< 8|a22<< 9|a21<<10|a20<<11|a19<<12|a18<<13|a17<<14|a16<<15| \
a15<<16|a14<<17|a13<<18|a12<<19|a11<<20|a10<<21|a09<<22|a08<<23| \
a07<<24|a06<<25|a05<<26|a04<<27|a03<<28|a02<<29|a01<<30|(unsigned)a00<<31)

Использование довольно просто =)

Ответ 13

Один, немного ужасный способ, которым вы могли бы это сделать, - создать файл .h с большим количеством #defines...

#define b00000000 0
#define b00000001 1
#define b00000010 2
#define b00000011 3
#define b00000100 4

и т.д.. Это может иметь смысл для 8-битных чисел, но, вероятно, не для 16-бит или более.

В качестве альтернативы сделайте это (похоже на ответ Заха Скривена):

#define bit(x) (1<<x)
int HAS_NUKES       = bit(HAS_NUKES_OFFSET);
int HAS_BIO_WEAPONS = bit(HAS_BIO_WEAPONS_OFFSET);

Ответ 14

Нет синтаксиса для литерала бинарных констант в С++, так как существует шестнадцатеричный и восьмеричный. Самое близкое для того, на что похоже, что вы пытаетесь сделать, вероятно, состояло бы в том, чтобы изучить и использовать bitset.

Ответ 15

В стороне:

Особенно, если вы имеете дело с большим набором, вместо того, чтобы проходить [незначительное] умственное усилие написания последовательности сумм сдвига, вы можете заставить каждую константу зависеть от ранее определенной константы:

const int has_nukes        = 1;
const int has_bio_weapons  = has_nukes        << 1;
const int has_chem_weapons = has_bio_weapons  << 1;
const int has_nunchuks     = has_chem_weapons << 1;
// ...

Выглядит немного избыточно, но это менее опечатка. Кроме того, вы можете просто вставить новую константу посередине без необходимости касаться любой другой строки, кроме той, которая сразу же после нее:

const int has_nukes        = 1;
const int has_gravity_gun  = has_nukes        << 1; // added
const int has_bio_weapons  = has_gravity_gun  << 1; // changed
const int has_chem_weapons = has_bio_weapons  << 1; // unaffected from here on
const int has_nunchuks     = has_chem_weapons << 1;
// ...

Сравнить с:

const int has_nukes        = 1 << 0;
const int has_bio_weapons  = 1 << 1;
const int has_chem_weapons = 1 << 2;
const int has_nunchuks     = 1 << 3;
// ...
const int has_scimatar     = 1 << 28;
const int has_rapier       = 1 << 28; // good luck spotting this typo!
const int has_katana       = 1 << 30;

и

const int has_nukes        = 1 << 0;
const int has_gravity_gun  = 1 << 1;  // added
const int has_bio_weapons  = 1 << 2;  // changed
const int has_chem_weapons = 1 << 3;  // changed
const int has_nunchuks     = 1 << 4;  // changed
// ...                                // changed all the way
const int has_scimatar     = 1 << 29; // changed *sigh*
const int has_rapier       = 1 << 30; // changed *sigh* 
const int has_katana       = 1 << 31; // changed *sigh*

В стороне от меня в стороне, вероятно, так же сложно обнаружить такую ​​опечатку:

const int has_nukes        = 1;
const int has_gravity_gun  = has_nukes        << 1;
const int has_bio_weapons  = has_gravity_gun  << 1;
const int has_chem_weapons = has_gravity_gun  << 1; // oops!
const int has_nunchuks     = has_chem_weapons << 1;

Итак, я думаю, что основное преимущество этого каскадного синтаксиса заключается в работе с вставками и удалениями констант.

Ответ 16

Другой метод:

template<unsigned int N>
class b
{
public:
    static unsigned int const x = N;

    typedef b_<0>  _0000;
    typedef b_<1>  _0001;
    typedef b_<2>  _0010;
    typedef b_<3>  _0011;
    typedef b_<4>  _0100;
    typedef b_<5>  _0101;
    typedef b_<6>  _0110;
    typedef b_<7>  _0111;
    typedef b_<8>  _1000;
    typedef b_<9>  _1001;
    typedef b_<10> _1010;
    typedef b_<11> _1011;
    typedef b_<12> _1100;
    typedef b_<13> _1101;
    typedef b_<14> _1110;
    typedef b_<15> _1111;

private:
    template<unsigned int N2>
    struct b_: public b<N << 4 | N2> {};
};

typedef b<0>  _0000;
typedef b<1>  _0001;
typedef b<2>  _0010;
typedef b<3>  _0011;
typedef b<4>  _0100;
typedef b<5>  _0101;
typedef b<6>  _0110;
typedef b<7>  _0111;
typedef b<8>  _1000;
typedef b<9>  _1001;
typedef b<10> _1010;
typedef b<11> _1011;
typedef b<12> _1100;
typedef b<13> _1101;
typedef b<14> _1110;
typedef b<15> _1111;

Использование:

std::cout << _1101::_1001::_1101::_1101::x;

Реализовано в CityLizard ++ (citylizard/binary/b.hpp).

Ответ 17

Если вы хотите использовать биты, автоматические, вариативные шаблоны, пользовательские литералы, static_assert, constexpr и noexcept, попробуйте это:

template<char... Bits>
  struct __checkbits
  {
    static const bool valid = false;
  };

template<char High, char... Bits>
  struct __checkbits<High, Bits...>
  {
    static const bool valid = (High == '0' || High == '1')
                   && __checkbits<Bits...>::valid;
  };

template<char High>
  struct __checkbits<High>
  {
    static const bool valid = (High == '0' || High == '1');
  };

template<char... Bits>
  inline constexpr std::bitset<sizeof...(Bits)>
  operator"" bits() noexcept
  {
    static_assert(__checkbits<Bits...>::valid, "invalid digit in binary string");
    return std::bitset<sizeof...(Bits)>((char []){Bits..., '\0'});
  }

Используйте его следующим образом:

int
main()
{
  auto bits = 0101010101010101010101010101010101010101010101010101010101010101bits;
  std::cout << bits << std::endl;
  std::cout << "size = " << bits.size() << std::endl;
  std::cout << "count = " << bits.count() << std::endl;
  std::cout << "value = " << bits.to_ullong() << std::endl;
  //  This triggers the static_assert at compile-time.
  auto badbits = 2101010101010101010101010101010101010101010101010101010101010101bits;
  //  This throws at run-time.
  std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101bits");
}

Благодаря @johannes-schaub-litb