C > bit演算 > 20bitの数値から任意の開始、終了位置の値を読取る

```txt:動作環境
C
```

## 仕様

- 20bitの数値がある
- MSBのインデックスを0とする
    - LSBはインデックスは19となる
- 開始,終了のインデックスを与えた時、その部分の値を取得したい

## v0.1

- 終了位置まで右シフトする
- ビット数で&演算をする

http://ideone.com/F4qmdm

```c
#include <stdio.h>

#define OUT_OF_RANGE (-1)
#define MAX_BITS (20)

int getBitFilled(int bitnum)
{
	bitnum--;
	return ((2 << bitnum)-1);
}

int extractBits(int start, int end, int bit20val)
{
	if (start < 0 || end > MAX_BITS) {
		return OUT_OF_RANGE; // error
	}
	int work = bit20val;
	int numbit = (end - start + 1);
	if (end > 0) {
		work >>= (20 - end - 1);
	}
	return work & getBitFilled(numbit);
}

void Test_extractBits(int start, int end, int bit20val)
{
	int res = extractBits(start, end, bit20val);
	printf("%d\n", res);
}

int main(void) {
	int res;
	
	Test_extractBits(18, 19, /*bit20val=*/0b11);   // 3
	Test_extractBits(17, 18, /*bit20val=*/0b1011); // 1
	Test_extractBits(16, 17, /*bit20val=*/0b1011); // 2
	Test_extractBits(0, 4, /*bit20val=*/(2<<15));  // 2
	return 0;
}

```

```txt:run
3
1
2
2
```

Windowsの電卓で確認したところ、大きく間違えてはいないよう。

テストが網羅されていないが、とりあえず。

## v0.2 
(追記 2017/07/27)

- getBitfilled()の処理を訂正
- OUT_OF_RANGE判定の間違い訂正 (MAX_BITS関連)

http://ideone.com/8SMB7C

```c
#include <stdio.h>

#define OUT_OF_RANGE (-1)
#define MAX_BITS (20)

int getBitFilled(int bitnum)
{
	return ((1 << bitnum)-1);
}

int extractBits(int start, int end, int bit20val)
{
	if (start < 0 || end >= MAX_BITS) {
		return OUT_OF_RANGE; // error
	}
	int work = bit20val;
	int numbit = (end - start + 1);
	if (end > 0) {
		work >>= (20 - end - 1);
	}
	return work & getBitFilled(numbit);
}

void Test_extractBits(int start, int end, int bit20val)
{
	int res = extractBits(start, end, bit20val);
	printf("%d\n", res);
}

int main(void) {
	int res;
	
	Test_extractBits(18, 19, /*bit20val=*/0b11);   // 3
	Test_extractBits(17, 18, /*bit20val=*/0b1011); // 1
	Test_extractBits(16, 17, /*bit20val=*/0b1011); // 2
	Test_extractBits(0, 4, /*bit20val=*/(2<<15));  // 2
	return 0;
}
```

テストはまだ足りない。

## v0.3

- print_binary()追加
- assert()追加
- テストを増やした
- `end=-1`の場合、LSBまで読むように変更

Tool: テストデータ(748118)は http://calc.50x.eu/ にてランダムにビットをON/OFFして作成

http://ideone.com/kvKQzd

```c
#include <stdio.h>
#include <assert.h>

#define OUT_OF_RANGE (-1)
#define MAX_BITS (20)

void print_binary(int number, int num_digits) {
    int digit;
    for(digit = num_digits - 1; digit >= 0; digit--) {
        putchar(number & (1 << digit) ? '1' : '0');
        if (digit > 0 && digit % 4 == 0)
            putchar(' ');
    }
}

int getBitFilled(int bitnum)
{
	return ((1 << bitnum)-1);
}

int extractBits(int start, int end, int bit20val)
{
	if (start < 0 || end >= MAX_BITS) {
		return OUT_OF_RANGE; // error
	}
	int work = bit20val;
	int numbit;
	if (end >= 0) {
		numbit = (end - start + 1);
	} else {
		numbit = (MAX_BITS - start + 1);		
	}
	if (end > 0) {
		work >>= (20 - end - 1);
	}
	return work & getBitFilled(numbit);
}

void Test_extractBits(int start, int end, int bit20val, int answer)
{
	int res = extractBits(start, end, bit20val);
	printf("%7d:", res);
	print_binary(res, 20);
	printf("\n");
	assert(res == answer);
}

int main(void) {
	int res;
	
	//            (start, end, bit20val, answer)
	Test_extractBits(18, 19, 0b11, 3);
	Test_extractBits(17, 18, 0b1011, 1);
	Test_extractBits(16, 17, 0b1011, 2);
	Test_extractBits( 0,  4, (2<<15), 2);
	printf("\n");
	Test_extractBits(-1,  4, (2<<15), -1); // (OUT_OF_RANGE)
	Test_extractBits( 0, 20, (2<<15), -1); // (OUT_OF_RANGE)
	printf("\n");
	//             0123 4567 8901 2345 6789
	// 748118: (0b 1011 0110 1010 0101 0110)
	Test_extractBits( 0,  2, 748118, 5); // 0b101
	Test_extractBits( 3,  8, 748118, 45); // 0b101101
	Test_extractBits( 9, 15, 748118, 37); // 0b0100101
	Test_extractBits( 16, 19, 748118, 6); // 0b0110
	printf("\n");
	Test_extractBits( 0, -1, 748118, 748118);
	Test_extractBits(17, -1, 748118, 6); // 0b110
	return 0;
}

```

```txt:run
      3:0000 0000 0000 0000 0011
      1:0000 0000 0000 0000 0001
      2:0000 0000 0000 0000 0010
      2:0000 0000 0000 0000 0010

     -1:1111 1111 1111 1111 1111
     -1:1111 1111 1111 1111 1111

      5:0000 0000 0000 0000 0101
     45:0000 0000 0000 0010 1101
     37:0000 0000 0000 0010 0101
      6:0000 0000 0000 0000 0110

 748118:1011 0110 1010 0101 0110
      6:0000 0000 0000 0000 0110
```

用意したテストは全assertを通った。

20bitを超える値がbit20valに入った場合は、&演算で最大20bitまでしか扱われないので、特に対処していない。

## v0.4 > 修正

- fix bug > extractBits()において`end=-1`の時のnumbit計算が間違い
- テストを変更した

http://ideone.com/CczrEF

```c
#include <stdio.h>
#include <assert.h>

#define OUT_OF_RANGE (-1)
#define MAX_BITS (20)

void print_binary(int number, int num_digits) {
    int digit;
    for(digit = num_digits - 1; digit >= 0; digit--) {
        putchar(number & (1 << digit) ? '1' : '0');
        if (digit > 0 && digit % 4 == 0)
            putchar(' ');
    }
}

int getBitFilled(int bitnum)
{
	return ((1 << bitnum)-1);
}

int extractBits(int start, int end, int bit20val)
{
	if (start < 0 || end >= MAX_BITS) {
		return OUT_OF_RANGE; // error
	}
	int work = bit20val;
	int numbit;
	if (end >= 0) {
		numbit = (end - start + 1);
	} else {
		numbit = (MAX_BITS - start); // fix		
	}
	if (end > 0) {
		work >>= (20 - end - 1);
	}
	return work & getBitFilled(numbit);
}

void Test_extractBits(int start, int end, int bit20val, int answer)
{
	int res = extractBits(start, end, bit20val);
	printf("%7d:", res);
	print_binary(res, 20);
	printf("\n");
	assert(res == answer);
}

int main(void) {
	int res;
	
	//              0123 4567 8901 2345 6789
	// 1048575: (0b 1111 1111 1111 1111 1111)
	//            (start, end, bit20val, answer)
	// 1. out of range case
	Test_extractBits(-1,  4, 1048575, -1); // (OUT_OF_RANGE)
	Test_extractBits( 0, 20, 1048575, -1); // (OUT_OF_RANGE)
	printf("\n");
	// 2. in range case
	Test_extractBits( 0,   2, 1048575, getBitFilled(3));
	Test_extractBits( 3,   10, 1048575, getBitFilled(8));
	Test_extractBits( 11,   19, 1048575, getBitFilled(9));
	Test_extractBits( 0,   19, 1048575, getBitFilled(20));
	printf("\n");
	// 3. end=-1 case
	Test_extractBits( 0,  -1, 1048575, getBitFilled(20));
	Test_extractBits( 17, -1, 1048575, getBitFilled(3));

	//              0123 4567 8901 2345 6789
	//  986895: (0b 1111 0000 1111 0000 1111)
	// 4. in range case
	Test_extractBits( 0,  4, 986895, getBitFilled(4) << 1);
	Test_extractBits( 3,  8, 986895, 0b100001);
	Test_extractBits( 9, 17, 986895, 0b111000011);
	return 0;
}
```

```txt:run
     -1:1111 1111 1111 1111 1111
     -1:1111 1111 1111 1111 1111

      7:0000 0000 0000 0000 0111
    255:0000 0000 0000 1111 1111
    511:0000 0000 0001 1111 1111
1048575:1111 1111 1111 1111 1111

1048575:1111 1111 1111 1111 1111
      7:0000 0000 0000 0000 0111
     30:0000 0000 0000 0001 1110
     33:0000 0000 0000 0010 0001
    451:0000 0000 0001 1100 0011
```