Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > двоичная логика. исключить X из Y.


Автор: boostcoder 26.12.2011, 20:47
всем привет.

наверное не для кого не секрет, что у меня сложности с двоичными операциями. посему, решил поинтересоваться у более опытных.

имеется такой код:
Код

#include <iostream>

enum mye {
   one = 0x02,
   two = 0x04,
   three = 0x08
};

void test( int arg) {
   if ( arg & one ) {
      std::cout << "one ";
   }
   if ( arg & two ) {
      std::cout << "two ";
   } 
   if ( arg & three ) {
      std::cout << "three ";
   }
   
   std::cout << std::endl;
}

int main() {
   test(one);
   test(two);
   test(two|one);
   test(two|three);
   test(one|three);
   test(one|three|two);
}

http://liveworkspace.org/code/c8c0c813081236f15b66bc14a01b8a91

в этом коде мне не нравится то, что в функции test() для обработки аргумента необходимо писать if для всех членов определенного енума.
вопроса два:
1. каким образом можно записать это в виде цикла так, чтоб в цикле перебрать и проверить все комбинации?
2. что нужно сделать с аргументом для того чтоб его можно было передать в switch() ?

благодарен.

Автор: kuzulis 26.12.2011, 21:09
А ничего тут и не сделать особо. Текущий вариант самый быстрый и правильный, ИМХО.
Всякие циклы и т.п. только загромоздят.

Можно на крайняк так:

Код

enum mye {
   one = 0x02,
   two = 0x04,
   three = 0x08
};

void test( int arg) {
    int i = 1;
    do {
        arg >>= i++;
        if (arg & 1)
            std::cout << i;
    } while (arg);
}


Автор: feodorv 26.12.2011, 21:31
Так подойдёт?

Код

#include <iostream>

const unsigned int start = 1;
const unsigned int end = 4;

const char *names[] =
{
  "one ",
  "two ",
  "three ",
  NULL
};

void test( unsigned int arg) {
   for( unsigned int i = start; i < end; i++)
      if( arg & (0x1u<<i) ) {
         std::cout << names[i-start];
      }
   std::cout << std::endl;
}

enum mye {
   one = 0x02,
   two = 0x04,
   three = 0x08
};

int main() {
   test(one);
   test(two);
   test(two|one);
   test(two|three);
   test(one|three);
   test(one|three|two);
}

Автор: boostcoder 26.12.2011, 22:16
kuzulis, feodorv, неее, рыбятушки. что-то это все не то. у меня было два вопроса. ответ на первый я почти получил. но и он какой-то не логичный, как мне кажется..

вот я упаковываю значения при помощи |(or). в результате, я получаю что-то типа множества. я полагал, что по этому множеству можно как-то проитерироваться. не?

Добавлено @ 22:18
feodorv, не могли бы Вы словами рассказать, что тут происходит?:
Цитата(feodorv @  26.12.2011,  21:31 Найти цитируемый пост)
arg & (0x1u<<i)


Автор: feodorv 26.12.2011, 23:04
Цитата(boostcoder @  26.12.2011,  22:16 Найти цитируемый пост)
у меня было два вопроса

Цитата(boostcoder @  26.12.2011,  20:47 Найти цитируемый пост)
что нужно сделать с аргументом для того чтоб его можно было передать в switch()

Вопрос я совсем не понял, даже приблизительно. Была надежда, что из ответа на первый можно будет вывести второй, но нет)))

Цитата(boostcoder @  26.12.2011,  22:16 Найти цитируемый пост)
не могли бы Вы словами рассказать, что тут происходит?

Происходит побитовый сдвиг влево изначальной единички на нужное число позиций:
Код

0x1u << 0 даёт 0x1u
0x1u << 1 даёт 0x2u
0x1u << 2 даёт 0x4u
0x1u << 3 даёт 0x8u


Цитата(boostcoder @  26.12.2011,  22:16 Найти цитируемый пост)
по этому множеству можно как-то проитерироваться

Вот как раз и происходит обход всех возможных битов smile

Добавлено через 4 минуты и 33 секунды
Цитата(boostcoder @  26.12.2011,  20:47 Найти цитируемый пост)
что нужно сделать с аргументом для того чтоб его можно было передать в switch()

Аааа smile Возможно, я понял:
Код

int i;
if( arg == 0 ) return ???;
for( i = 0; i < 32; i++) if( arg & (0x1u << i) ) break;
switch( i )
{
  case 0:
    ...
  case 1:
    ...
}


Добавлено через 8 минут и 57 секунд
Или:
Код

int i = 0;
while( (arg & 0x1u) == 0 ){ arg >>= 1; i++; }

Только тут уж железно arg на 0 предварительно проверить нужно.

Автор: newbee 26.12.2011, 23:45
Если сложно вдуплить битовые сдвиги, можно запихать все возможные маски в массив и пройтись по нему.

Код

x=011100001
masks={one,two,four,...}
foreach(m in masks)
 if(x&m)
  std::cout<<m


Выше я очень бегло посмотрела примеры, как-то сложно...

Код

x=0001010110
for(size_t i=1;;i=i<<1){ // сдвигаем i на один бит, из 1 получаем 2, из 2 - 4, из 4 - 8 и т.д.
 if(x&i)
  std::cout<<i;
 if(i==last_mask) // проверяем i на значение самой "большой" маски
  break;}

Автор: mes 27.12.2011, 02:26
Цитата(boostcoder @  26.12.2011,  19:47 Найти цитируемый пост)
. каким образом можно записать это в виде цикла так, чтоб в цикле перебрать и проверить все комбинации?


boostcoder, 
http://cplusplus.com/reference/stl/bitset/test/
или
http://cplusplus.com/reference/stl/bitset/operator[]/

в дополение :
http://cplusplus.com/reference/stl/bitset/count/
http://cplusplus.com/reference/stl/bitset/any/
http://cplusplus.com/reference/stl/bitset/none/

Автор: boostcoder 27.12.2011, 20:38
mes, не нужен мне битсет.

всем спасибо. сейчас попробую все это дело усвоить...

Автор: mes 28.12.2011, 01:42
Цитата(boostcoder @  27.12.2011,  19:38 Найти цитируемый пост)
, не нужен мне битсет.

может и не нужен, но может служить выходом для тех, кому трудно обращаться с битами..
ваш первый пример : http://liveworkspace.org/code/6d76dce75302d93f1a78172b15ba5b99

Автор: boostcoder 28.12.2011, 02:04
Цитата(mes @  28.12.2011,  01:42 Найти цитируемый пост)
может служить выходом для тех, кому трудно обращаться с битами..

ну так я пытаюсь разобраться, а Вы меня обратно, в мое же болото smile

Автор: mes 28.12.2011, 02:59
Цитата(boostcoder @  28.12.2011,  01:04 Найти цитируемый пост)
ну так я пытаюсь разобраться, а Вы меня обратно, в мое же болото 

ну разбирайтесь, однако с битсетом иногда надежнее )


Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)