入力パラメータに基づいてビットマスクを生成するというこの独特の問題に直面していました。例えば、
param = 2の場合、マスクは0x3(11b)になり、param = 5の場合、マスクは0x1F(1 1111b)になります
これは、Cのforループを使用して実装しました。
int nMask = 0;
for (int i = 0; i < param; i ++) {
nMask |= (1 << i);
}
より良いアルゴリズムがあるかどうか知りたいです~~~
そのようなビットマスクについて注意すべき1つのことは、それらが常に2のべき乗よりも1つ少ないということです。
式1 << n
は、2のn乗を取得する最も簡単な方法です。
Zeroが00000001
のビットマスクを提供したくない場合、ゼロを提供したい場合。したがって、1を減算する必要があります。
mask = (1 << param) - 1;
編集:
Param> 32の特殊なケースが必要な場合:
int sizeInBits = sizeof(mask) * BITS_PER_BYTE; // BITS_PER_BYTE = 8;
mask = (param >= sizeInBits ? -1 : (1 << param) - 1);
このメソッドは16、32、または64ビットの整数で機能しますが、明示的に「1」を入力する必要がある場合があります。
C:
#include <limits.h> /* CHAR_BIT */
#define BIT_MASK(__TYPE__, __ONE_COUNT__) \
((__TYPE__) (-((__ONE_COUNT__) != 0))) \
& (((__TYPE__) -1) >> ((sizeof(__TYPE__) * CHAR_BIT) - (__ONE_COUNT__)))
C++:
#include <climits>
template <typename R>
static constexpr R bitmask(unsigned int const onecount)
{
// return (onecount != 0)
// ? (static_cast<R>(-1) >> ((sizeof(R) * CHAR_BIT) - onecount))
// : 0;
return static_cast<R>(-(onecount != 0))
& (static_cast<R>(-1) >> ((sizeof(R) * CHAR_BIT) - onecount));
}
BIT_MASK(unsigned int, 4) /* = 0x0000000f */
BIT_MASK(uint64_t, 26) /* = 0x0000000003ffffffULL */
#include <stdio.h>
int main()
{
unsigned int param;
for (param = 0; param <= 32; ++param)
{
printf("%u => 0x%08x\n", param, BIT_MASK(unsigned int, param));
}
return 0;
}
0 => 0x00000000
1 => 0x00000001
2 => 0x00000003
3 => 0x00000007
4 => 0x0000000f
5 => 0x0000001f
6 => 0x0000003f
7 => 0x0000007f
8 => 0x000000ff
9 => 0x000001ff
10 => 0x000003ff
11 => 0x000007ff
12 => 0x00000fff
13 => 0x00001fff
14 => 0x00003fff
15 => 0x00007fff
16 => 0x0000ffff
17 => 0x0001ffff
18 => 0x0003ffff
19 => 0x0007ffff
20 => 0x000fffff
21 => 0x001fffff
22 => 0x003fffff
23 => 0x007fffff
24 => 0x00ffffff
25 => 0x01ffffff
26 => 0x03ffffff
27 => 0x07ffffff
28 => 0x0fffffff
29 => 0x1fffffff
30 => 0x3fffffff
31 => 0x7fffffff
32 => 0xffffffff
まず、他の回答ですでに説明したように、シフトカウントが値のストレージタイプのビット数に等しい場合の問題を防ぐために、>>
の代わりに<<
が使用されます。 (ありがとう 上記のジュリアンの答え アイデア)
議論を簡単にするために、unsigned int
を__TYPE__
としてマクロを「インスタンス化」し、何が起こるかを見てみましょう(現時点では32ビットと仮定):
((unsigned int) (-((__ONE_COUNT__) != 0))) \
& (((unsigned int) -1) >> ((sizeof(unsigned int) * CHAR_BIT) - (__ONE_COUNT__)))
焦点を当てましょう:
((sizeof(unsigned int) * CHAR_BIT)
最初。 sizeof(unsigned int)
はコンパイル時に既知です。仮定によれば、これは4
と同じです。 CHAR_BIT
は、char
あたりのビット数を表します。別名はバイトあたりです。コンパイル時にも知られています。これは、地球上のほとんどのマシンで8
と同等です。この式はコンパイル時に既知であるため、コンパイラはおそらくコンパイル時に乗算を行い、定数として処理します。これは、この場合は32
に等しくなります。
に移動しましょう:
((unsigned int) -1)
0xFFFFFFFF
と同じです。 -1
を任意の符号なしの型にキャストすると、その型で「all-1s」の値が生成されます。この部分もコンパイル時定数です。
これまで、式:
(((unsigned int) -1) >> ((sizeof(unsigned int) * CHAR_BIT) - (__ONE_COUNT__)))
実際には次と同じです:
0xffffffffUL >> (32 - param)
上記のジュリアンの答えと同じです。彼の答えの1つの問題は、param
が0
に等しく、式0xffffffffUL >> 32
を生成する場合、式の結果は期待される0xffffffffUL
ではなく0
になることです! (そのため、パラメーターを__ONE_COUNT__
と名付け、その意図を強調しています)
この問題を解決するには、次のように__ONE_COUNT
または0
を使用してif-else
が?:
に等しい特別なケースを追加するだけです。
#define BIT_MASK(__TYPE__, __ONE_COUNT__) \
(((__ONE_COUNT__) != 0) \
? (((__TYPE__) -1) >> ((sizeof(__TYPE__) * CHAR_BIT) - (__ONE_COUNT__)))
: 0)
しかし、ブランチフリーのコードのほうがクールですね。次の部分に移りましょう:
((unsigned int) (-((__ONE_COUNT__) != 0)))
一番内側の表現から一番外側の表現まで始めましょう。 ((__ONE_COUNT__) != 0)
は、パラメーターが0
の場合は0
を生成し、そうでない場合は1
を生成します。 (-((__ONE_COUNT__) != 0))
は、パラメーターが0
の場合は0
を生成し、そうでない場合は-1
を生成します。 ((unsigned int) (-((__ONE_COUNT__) != 0)))
については、タイプキャストのトリック((unsigned int) -1)
については既に説明しました。あなたは今トリックに気づいていますか?表現:
((__TYPE__) (-((__ONE_COUNT__) != 0)))
__ONE_COUNT__
がゼロの場合は「all-0s」、それ以外の場合は「all-1s」に等しくなります。これは、最初のステップで計算した値のビットマスクとして機能します。したがって、__ONE_COUNT__
がゼロ以外の場合、マスクは効果がなく、ジュリアンの答えと同じです。 __ONE_COUNT__
が0
の場合、Julienの答えのすべてのビットをマスクし、定数ゼロを生成します。視覚化するには、これを見てください:
__ONE_COUNT__ : 0 Other
------------- --------------
(__ONE_COUNT__) 0 = 0x000...0 (itself)
((__ONE_COUNT__) != 0) 0 = 0x000...0 1 = 0x000...1
((__TYPE__) (-((__ONE_COUNT__) != 0))) 0 = 0x000...0 -1 = 0xFFF...F
または、右シフトを使用して、(1 << param) - 1
解決。
unsigned long const mask = 0xffffffffUL >> (32 - param);
仮定して param <= 32
、 もちろん。
これについては(Javaの場合):
int mask = -1;
mask = mask << param;
mask = ~mask;
これにより、ルックアップテーブルと整数の長さのハードコーディングを回避できます。
説明:値が-1の符号付き整数は、すべて1としてバイナリーで表されます。指定された回数だけ左にシフトして、右側にその数の0を追加します。これにより、種類の「逆マスク」が発生します。次に、シフトした結果を否定してマスクを作成します。
これは次のように短縮できます。
int mask = ~(-1<<param);
例:
int param = 5;
int mask = -1; // 11111111 (shortened for example)
mask = mask << param; // 11100000
mask = ~mask; // 00011111
(1 << param) - 1
を使用したCライクな言語のオーバーフローが心配な場合(maxsizeタイプでparamが32または64の場合、ビットシフトはtypeの境界を超えてプッシュされるため、マスクは0になります)の:
const uint32_t mask = ( 1ul << ( maxBits - 1ul ) ) | ( ( 1ul << ( maxBits - 1ul ) ) - 1ul );
または別の例
const uint64_t mask = ( 1ull << ( maxBits - 1ull ) ) | ( ( 1ull << ( maxBits - 1ull ) ) - 1ull );
これはテンプレート化されたバージョンです。これを符号なしのタイプRで使用する必要があることに注意してください。
#include <limits.h> /* CHAR_BIT */
// bits cannot be 0
template <typename R>
static constexpr R bitmask1( const R bits )
{
const R one = 1;
assert( bits >= one );
assert( bits <= sizeof( R ) * CHAR_BIT );
const R bitShift = one << ( bits - one );
return bitShift | ( bitShift - one );
}
最大ビットが1バイトで8であり、最初のオーバーフロー関数では1 << 8 == 256
があり、バイトにキャストすると0になるとしましょう。私の関数では1 << 7 == 128
があります。 1<<7 | 1<<7 - 1
になります。
関数をコンパイルしていないため、タイプミスが含まれている可能性があります。
そして、楽しみのためにここに Julien Royer の肉体があります:
// bits can be 0
template <typename R>
static constexpr R bitmask2( const R bits )
{
const R zero = 0;
const R mask = ~zero;
const R maxBits = sizeof( R ) * CHAR_BIT;
assert( bits <= maxBits );
return mask >> ( maxBits - bits );
}