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)





Randall Hyde、鵜飼 文敏、まつもと ゆきひろ、後藤 正徳、トップスタジオ