WGC1 第3章2進数の算術演算子とビット演算 3.6ビットフィールドとデータのパック
一般に、CPUの処理が最も効率よく行われるのは、バイト、ワード、ダブルワードの各データに対してである。
しかし、ときには、8ビット、16ビット、32ビットのいずれでもデータ型を処理するとが必要となる。
その場合には、ビット列をパックすると、メモリを節約できることがある。
これは、異なるビット列をできるだけコンパクトに取りまとめ、バイトまたは、
その他の境界に合わせて、ビットの無駄がまったくないような形で特定のデータフィールドを配置する
ということです。
例04/02/01 (月/日/年)を表すには、3つの数値が必要。
データ | 範囲 | ビット数 |
---|---|---|
月 | 1〜12 | ビット |
日 | 1〜31 | 5ビット |
年 | 0〜99 | 7ビット |
4+5+7=16ビット=2バイト
各データに1バイトずつしようした場合は、3バイト必要だが、日付データをパックすれば2バイトに収まる。
15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
M | M | M | M | D | D | D | D | D | Y | Y | Y | Y | Y | Y | Y |
各データ項目を表すビットの集まりそれぞれをビットフィールドという。
パックした値は、領域の面では効率がよいが、演算の面では、非効率。
なぜなら、さまざまなビットフィールドからデータをアンパックするための余計な命令が必要だから。
実験コード
#include <stdio.h> #include <stdlib.h> #define MONTH_SIZE (4) #define DAY_SIZE (5) #define YEAR_SIZE (7) int main(int argc, char* argv[]){ int input; unsigned char day = '\0'; unsigned char month = '\0'; unsigned char year = '\0'; unsigned char packed_day = '\0'; unsigned char packed_month = '\0'; unsigned char packed_year = '\0'; short packed_data = 0; if ( argc != 4) { printf("usage:bitfield_and_datapack month day year\n"); return 1; } input = atoi(argv[1]); if ( (input > 12) || (input < 1)) { printf("Month value is too large (%d)\n", input); return 1; } month = (unsigned char)input; input = atoi(argv[2]); if ( (input > 31) || (input < 1)) { printf("day value is too large (%d)\n", input); return 1; } day = (unsigned char)input; input = atoi(argv[3]); if ( (input > 99) || (input < 1)) { printf("year value is too large (%d)\n", input); return 1; } year = (unsigned char)input; /* pack ex 2009/1/30 month 1 = 0001 day 30 = 0001_1110 year 9 = 1001 1)month= 0001 2)DAY_SIZE left shift 0010_0000 3)OR day 0010_0000 0001_1110 ---------- 0011_1110 4) YEAR_SIZE left shift 0001_1111_0000_0000 5)or year 0001_1111_0000_0000 1001 --------------------- 0001_1111_0000_1001 packed_data 0001_1111_0000_1001 */ packed_data = month << DAY_SIZE ; packed_data = (day | packed_data) << YEAR_SIZE ; packed_data = (year | packed_data); /* unpack 0x7F= 0111_1111 0x1F= 0001_1111 0x0F = 1111 1)packed_data AND 0x7F 0001_1111_0000_1001 0111_1111 -------------------- 0000_0000_0000_1001 =9 2)YEAR_SIZE right shift 0001_1111_0000_1001 | V 0000_0011_1110 3)packed_data AND 0x1F 0000_0011_1110 0001_1111 --------------- 0001_1110 =30 4)DAY_SIZE right shift 0000_0011_1110 | V 0000_0001 5)packed_data AND 0x0F 0000_0001 1111 ---------- 0001 =1 */ packed_year = packed_data & 0x7F; packed_data = packed_data >> YEAR_SIZE; packed_day = packed_data & 0x1F; packed_month = (packed_data >> DAY_SIZE) & 0x0F; printf("The data is %02d/%02d/%02d (month/day/year)\n", month, day, year); printf("The pakecd_data is %02d/%02d/%02d (month/day/year)\n", packed_month, packed_day, packed_year); return 0; }
コンパイル
[oc@centos5 datapack]$ cc bitfield_and_datapack.c -o bitfield_and_datapack
結果
[oc@centos5 3.6_Bit_Fields_and_Packed_Data]$ bitfield_and_datapack 4 22 11 The data is 04/22/11 (month/day/year) The pakecd_data is 04/22/11 (month/day/year)