m設定ビットの前にk未設定ビットがあり、その後にn未設定ビットが続くビットマスクをCで構築する最良の方法は何ですか?
00..0 11..1 00..0
k m n
たとえば、k=1、m=4、n=3 の場合、ビット マスクは次のようになります。
01111000
m設定ビットの前にk未設定ビットがあり、その後にn未設定ビットが続くビットマスクをCで構築する最良の方法は何ですか?
00..0 11..1 00..0
k m n
たとえば、k=1、m=4、n=3 の場合、ビット マスクは次のようになります。
01111000
~(~0 << m) << n
では、k 個のリセット ビットの前に m 個のセット ビットがあり、その後に n 個のリセット ビットが続くことを要求していますか? k は、整数型の選択によって大きく制限されるため、無視できます。
mask = ((1 << m) - 1) << n;
私は両方のソリューションが好きです。これが私の頭に浮かぶ別の方法です(おそらく良くはありません)。
((~((unsigned int)0) << k) >> (k + n)) << n
編集:以前のバージョンにはバグがありました(unsigned intキャストがありませんでした)。問題は、~0 >> n0ではなく1を先頭に追加することでした。
そして、はい、このアプローチには1つの大きな欠点があります。デフォルトの整数型のビット数を知っていることを前提としています。つまり、実際にkを知っていることを前提としていますが、他のソリューションはkに依存していません。これにより、私のバージョンの移植性が低下するか、少なくとも移植が困難になります。(また、3つのシフト、加算、および2つの追加操作であるビット単位の否定演算子を使用します。)
したがって、他の例の1つを使用する方がよいでしょう。
さまざまなソリューションの出力を比較および検証するために、JonathanLefflerによって作成された小さなテストアプリを次に示します。
#include <stdio.h>
#include <limits.h>
enum { ULONG_BITS = (sizeof(unsigned long) * CHAR_BIT) };
static unsigned long set_mask_1(int k, int m, int n)
{
return ~(~0 << m) << n;
}
static unsigned long set_mask_2(int k, int m, int n)
{
return ((1 << m) - 1) << n;
}
static unsigned long set_mask_3(int k, int m, int n)
{
return ((~((unsigned long)0) << k) >> (k + n)) << n;
}
static int test_cases[][2] =
{
{ 1, 0 },
{ 1, 1 },
{ 1, 2 },
{ 1, 3 },
{ 2, 1 },
{ 2, 2 },
{ 2, 3 },
{ 3, 4 },
{ 3, 5 },
};
int main(void)
{
size_t i;
for (i = 0; i < 9; i++)
{
int m = test_cases[i][0];
int n = test_cases[i][1];
int k = ULONG_BITS - (m + n);
printf("%d/%d/%d = 0x%08lX = 0x%08lX = 0x%08lX\n", k, m, n,
set_mask_1(k, m, n),
set_mask_2(k, m, n),
set_mask_3(k, m, n));
}
return 0;
}
(のみ) BMI2 をサポートする x86 システム (Intel Haswell 以降、AMD Excavator 以降) でもう少し効率的なソリューションに関心がある場合:
mask = _bzhi_u32(-1,m)<<n;
このbzhi命令は、指定されたビット位置から始まる上位ビットをゼロにします。_bzhi_u32組み込みは、この命令にコンパイルされます。テストコード:
#include <stdio.h>
#include <x86intrin.h>
/* gcc -O3 -Wall -m64 -march=haswell bitmsk_mn.c */
unsigned int bitmsk(unsigned int m, unsigned int n)
{
return _bzhi_u32(-1,m)<<n;
}
int main() {
int k = bitmsk(7,13);
printf("k= %08X\n",k);
return 0;
}
出力:
$./a.out
k= 000FE000
コードフラグメント_bzhi_u32(-1,m)<<nは 3 つの命令にコンパイルされます
movl $-1, %edx
bzhi %edi, %edx, %edi
shlx %esi, %edi, %eax
これは、 @Jonathan Leffler
および@Darius Baconによるコードよりも 1 命令少ないものです。Intel Haswell プロセッサ以降では、 と の両方bzhiにshlx1 サイクルのレイテンシと 1 サイクルあたり 2 のスループットがあります。AMD Ryzen では、これら 2 つの命令のスループットは 1 サイクルあたり 4 です。
上位の回答はシンプルで効果的ですが、次の場合に MSB を設定しませn=0んm=31。
~(~0 << 31) << 0= <コード>0111 1111 1111 1111 1111 1111 1111 1111</コード>
((1 << 31)-1) << 0= <コード>0111 1111 1111 1111 1111 1111 1111 1111</コード>
32 ビット符号なしワードに対する私の提案は次のようになります。
unsigned int create_mask(unsigned int n,unsigned int m) {
// 0 <= start_bit, end_bit <= 31
assert(n >=0 && m<=31);
return (m - n == 31 ? ~0: ((1 << (m-n)+1)-1) << n);
}
これは実際には範囲内のビットを取得する[m,n]ため (閉区間) create_mask(0,0)、最初のビット (ビット 0)create_mask(4,6)のマスクを返し、ビット 4 から 6 のマスクを返します... 00111 0000。