質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

6回答

3186閲覧

ビット演算 と 個数

BeatStar

総合スコア4958

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2017/05/30 04:41

ビット演算に関してです。

C/C++でやっています。

ビット演算を用いて Windows API の CreateWindow関数に ウィンドウスタイルを渡すような感覚で

できたらなぁと思い、チャレンジしてみました。

しかしなぜかうまくいきません。

やったことは

  1. enumで(ビット演算用)定数定義

C++

1enum TEST_ENUM{ 2 TEST_DEFALUT = 0, 3 TEST_ENUM_TYPE1 = 1, 4 TEST_ENUM_TYPE2 = 2, 5 ... 6 TEST_ENUM_TYPE100 = 6 7}; 8

のように1~6 と デフォルトの 0 を定義します。

  1. unsigned int flag を引数として持つ関数 func に設定しながら渡す

C++

1// 定義部 2void func( unsigned int flag ){ 3 4 if( flag & TEST_ENUM_TYPE1 ) cout << "TEST_ENUM_TYPE1が定義されている" << endl; 5 6 if( flag & TEST_ENUM_TYPE2 ) cout << "TEST_ENUM_TYPE2が定義されている" << endl; 7 8 // 上記のやつを TEST_ENUM_TYPE6ぐらいまで。 9return; 10} 11 12// main関数等の呼び出し側 13func( TEST_ENUM_TYPE1 | TEST_ENUM_TYPE4 | TEST_ENUM_TYPE3 ); 14

としました。

これを動かしてみると、

TEST_ENUM_TYPE1が定義されている TEST_ENUM_TYPE2が定義されている TEST_ENUM_TYPE3が定義されている TEST_ENUM_TYPE4が定義されている ...

と立てていないフラグまで "立った" ことにされています。

なぜなのでしょうか?

Windows API だと 普通にできるのに...

順番がある?

でも CreateWindowに順番を逆にしても ( もちろん 一つの引数として ) 普通に想定した動きになります。

何がいけないのでしょうか?

もし

C++

1if( flag & TEST_ENUM_TYPE1 ){ 2 cout << "TEST_ENUM_TYPE1が定義されている" << endl; 3}else if( flag & TEST_ENUM_TYPE2 ){ 4 cout << "TEST_ENUM_TYPE2が定義されている" << endl; 5}else if( flag & TEST_ENUM_TYPE3 ){ 6 cout << "TEST_ENUM_TYPE3が定義されている" << endl; 7}else if( ,,, 8... 9}else{ // フラグは"全く"立っていない 10 cout << "フラグなし" << endl; 11}

とする方法もありそうですが、これフラグ用定数が2つしかなくても 2通り ( 個別 ) + 4通り ( 組み合わせ ) だし、

3つ以上だと 1個だで設定されている場合, 2個設定されている場合, 3個設定... とそれの和になるだろうし...

分離するだけでも 100行以上ありそう...

これを 一番最初にやった方法みたいにできたらいいのですが...
( そうすれば パターンは考えなくても良さそうだし。 ただ 組み合わせで 切り替えする場合は別として。 )

どのようにやればいいのでしょうか。

「C言語 ビット演算」 ( C++版も同様 ) のように検索かけたり、「C言語 ビット演算 Windows API」みたいに検索しても

演算そのもの ( "&" でチェック, "|" で追加, "~" で削除 etc. ) しか載っていない場合が多いし。

つまり、パターン ( 一個だけ設定されたとき, 二個設定されたとき, 三個設定されたとき...また それぞれの組み合わせ ) を考えなくても

if( flag & 定数 )

みたいにすれば いいみたいな方法です。

もうちょっと調べてみると

ビット演算を理解してフラグを使いこなす - C#

がヒットしました。

上記サイトはC#ですが、シフト演算子というのでしょうか。

"<<" を使って定義しています。

なので

1 を基準にして、1以降は 1に n 分( ぶん ) シフトしてやると

C++

1enum TEST_ENUM{ 2 TEST_DEFALUT = 0, 3 TEST_ENUM_TYPE1 = 1, 4 TEST_ENUM_TYPE2 = 1 << 1, 5 TEST_ENUM_TYPE3 = 1 << 2, 6 TEST_ENUM_TYPE4 = 1 << 3, 7 TEST_ENUM_TYPE5 = 1 << 4, 8 ... 9};

としたところ、処理自体はうまくいきました。

また

ロベールのC++教室
の「先ずは基本から」-> 「第49章 ビットでフラグ」でも大体同様のやり方。

( 後者は マクロ定数にしていますが。 )

しかし、上記 enumに「TEST_ENUM_TYPE101 = 1 << 100」を追加しようとすると

(ファイルパス+ファイル名):(行):(列?): warning: left shift count >= width of type [- shift-count-overflow] ...

とコンパイルエラー。

int ( 厳密には unsigned intですが。 ) なので 100 は 範囲内だと思うのですが...

もしかして こういうビット演算用定数 は 何十 ( 大体 30前後? ) までしかできないのでしょうか?

もし可能なら ファイル ( ディレクトリ を含んでもいいですが。 ) を列挙する関数 ( あるいはクラス? )を作成して、

「JPGファイル, BMPファイル, PNGファイル, GIFファイル ... を列挙対象にする」みたいに できるのですが...

拡張子は私が聞いたことがある ( よく使う ) ものだけでも 100ぐらいありそうですし。

( そこまではいかなくても、使える範囲が広がるし。 )

知りたいことは、簡単に言えば、

パターンを考えずに抽出&処理をし、また定数の個数が100や1000でも耐えうるビット演算方法

です。

[情報]
言語 : C/C++
WinAPI : あり ( ただ、今回はあまり関係ないかも... )
コンパイラ: MinGW ( 同上 )

宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

ビット演算はその名の通り、ビットごとの演算を行う演算方法です。
なのでビットに意味を持たせたい場合は、値毎にビットの重複がないように設定しないといけません。
従って定義している値が10進数で0~6になっているのが問題です。
これらを2進数で表すと以下のようになります。

0|000 1|001 2|010 3|011 4|100 5|101 6|110

ご覧のように例えば10進数の3は011なので12でONビットが重複しています。
通常、ビットに意味を持たせたい場合は、前述したようにONビットが重複しないように設定します。

0|000000 1|000001 2|000010 4|000100 8|001000 16|010000 32|100000

投稿2017/05/30 04:54

ttyp03

総合スコア16996

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

冒頭のenumの部分でお聞きしたいのですが、

enum TEST_ENUM{ TEST_DEFALUT = 0, TEST_ENUM_TYPE1 = 1, TEST_ENUM_TYPE2 = 2, ... TEST_ENUM_TYPE100 = 6 };

TEST_ENUM_TYPE3TEST_ENUM_TYPE4の定義を教えてください。

もし、以下のように書いているならばビットフラグの定義をもう一度確認して下さい。

enum TEST_ENUM{ TEST_DEFALUT = 0, TEST_ENUM_TYPE1 = 1, TEST_ENUM_TYPE2 = 2, TEST_ENUM_TYPE3 = 3, TEST_ENUM_TYPE4 = 4, TEST_ENUM_TYPE5 = 5, TEST_ENUM_TYPE100 = 6 };

正しく書くのであれば以下のようになります。

enum TEST_ENUM{ TEST_DEFALUT = 0, TEST_ENUM_TYPE1 = 1, TEST_ENUM_TYPE2 = 2, TEST_ENUM_TYPE3 = 4, TEST_ENUM_TYPE4 = 8, TEST_ENUM_TYPE5 = 16, ... };

ビットフラグの値は2の乗数でなければなりません、2進数の定義と&演算の定義を確認してください。

通常、2の乗数を計算するのは面倒なので、参考にしているC#のサイトのようにシフト演算するのが一般的です。

「パターンを考えずに抽出&処理をし、また定数の個数が100や1000でも耐えうるビット演算方法」

2の1000乗のパターンが必要なのでしょうか?これは天文学的な数値です。
通常、ファイルの拡張子を判別する用途であればビットフラグは使いません、拡張子はGIFでありTXTであるなどの組み合わせの状態がないので、ビットフラグの用途ではありません。

投稿2017/05/30 05:06

編集2017/05/30 05:08
pashango2

総合スコア930

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

BeatStar

2017/05/30 07:24

ご回答ありがとうございます。 >> ファイルの拡張子を判別する用途であ... "そのファイルがどの拡張子であるか" ではなく、 ファイルの列挙を行うときに 画像系拡張子と 動画系拡張子, および ディレクトリ みたいに指定する場合です。
pashango2

2017/05/30 08:32 編集

なるほど、理解しました! たぶん先程のコメントは的外れでしたので削除しました。 可変長のクエリーを表現したいということでしょうか? でしたら、普通にクエリーをリストコンテナなどを使い、可変長にしておけば良いと思います。
guest

0

コンピュータの内部では全ての数字は二進数で扱われています。ビット演算は,それらの二進数における各桁に対して行われる演算です。

ビット演算でフラグを管理するときは,一般的にiを整数としてi番目のビットだけが1になっているような数を使います。それはつまり,1をシフト演算したような値のことです。これは端的には 2 の累乗数です。

もし定数として A=3 (=011) や B=6 (=110) のような値を使ってしまうと,3 と 6 は真ん中のビットが共通して 1 なので,たとえば

  • あるフラグ変数にはAが設定されている。これを何らかの関数に渡す。
  • その関数では,
  • そのフラグ変数にBが設定されているか調べたい
  • Aがセットしてある変数に対し「BとAND演算」を行う
  • 結果に真ん中のビットが残ってしまう
  • 演算結果が非ゼロなのでこれは真とされる
  • その関数は「Bがセットされている」と誤判定する

というようなことが起こります。
これを防ぐためには各定数のビットがどこも被っていなければよいわけです。これを一番効率良くしようとすると「どこか一個だけ1が立っている数」が便利なので,2の累乗数が使われるわけです。

100と言うと小さそうに見えますが,100個の項目に対してそれぞれON,OFFが存在するとすると,それは計 2^100 通りもの状態がありえます。普通ならとうていintで数え上げられるような量ではありません。intが128bitの処理系ならば別ですが・・・。

投稿2017/05/30 05:24

編集2017/05/30 09:21
Eki

総合スコア429

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

BeatStar

2017/06/05 03:42

ご回答ありがとうございます。 なるほど。 そういう理由だったんですね。
guest

0

TEST_ENUM_TYPE_100 はビットが二つ立ってますね。
1000 種類のフラグが必要なら 1000 ビット必要です。

投稿2017/05/30 04:53

Zuishin

総合スコア28656

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

BeatStar

2017/06/05 03:43

もしかして 10進数?を 2進数 に変換する作業 ( 素因数分解でしたっけ? ) でやるあれですか。 数学苦手なのでそこは思いつきませんでした...
guest

0

intは10^9くらいで、1<<100は2^100 = 30桁となり、オーバーフローします。

投稿2017/05/30 04:57

_Victorique__

総合スコア1392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

「定数の個数が100や1000でも耐えうるビット演算方法」
として、bitsetはいかがですか?

C++

1 2#include <iostream> 3#include <bitset> 4using namespace std; 5 6enum TEST_ENUM 7{ 8 ENUM_DEFAULT = 0, 9 ENUM_ENUM997 = 997, 10 ENUM_ENUM998 = 998, 11 ENUM_ENUM999 = 999, 12 TEST_ENUM_MAX = 1000 13}; 14 15typedef bitset<TEST_ENUM_MAX> bigbit; 16 17void func(const bigbit& param) 18{ 19 if (param[ENUM_ENUM997]) { cout << "hit 997" << endl; } 20 if (param[ENUM_ENUM998]) { cout << "hit 998" << endl; } 21 if (param[ENUM_ENUM999]) { cout << "hit 999" << endl; } 22} 23 24 25int main() { 26 bigbit bits; 27 28 bits[ENUM_ENUM997] = 1; 29 bits[ENUM_ENUM999] = 1; 30 func(bits); 31 32 cout << bits.to_string() << endl; 33 34 return 0; 35} 36

投稿2017/05/31 11:09

ds-kawasaki

総合スコア35

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

BeatStar

2017/06/02 05:11

なるほど! std::bitset がありましたね。 使ったことないので 忘れてました。 今 手元にないので試すことができませんが、 とりあえず試してみます。
BeatStar

2017/06/05 03:41

帰宅してstd::bitsetを試してみました。 ハラショー! これいいですね! 今まではビット演算子の使い道とかがわからなかったり、 勘違い ( std::listや std::set みたいに iteratorを使うしかない等 ) していたのでノータッチでした...
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問