C で独自のタイムスタンプ データ構造を作成したいと考えています。
日 (0 - 30)、時間 (0 - 23)、分 (0 - 59)
可能な最小のデータ構造は何ですか?
さて、あなたはそれをすべて(2バイト、日は5ビット、時間は5ビット、分は6ビット)に詰めることができます...そしていくつかのシフトとマスキングを使用して値を取得します。unsigned short
unsigned short timestamp = <some value>; // Bits: DDDDDHHHHHMMMMMM
int day = (timestamp >> 11) & 0x1F;
int hour = (timestamp >> 6) & 0x1F;
int min = (timestamp) & 0x3F;
unsigned short dup_timestamp = (short)((day << 11) | (hour << 6) | min);
またはマクロを使用する
#define DAY(x) (((x) >> 11) & 0x1F)
#define HOUR(x) (((x) >> 6) & 0x1F)
#define MINUTE(x) ((x) & 0x3F)
#define TIMESTAMP(d, h, m) ((((d) & 0x1F) << 11) | (((h) & 0x1F) << 6) | ((m) & 0x3F)
(現在のバージョンの質問では月/年について言及していなかったため、省略しました)。
[編集:使用unsigned short
-署名されていませんshort
。]
HOUR0-23とMINUTE0-59のことですか?うるう秒について聞いたことがありますが、うるう分や時間については聞いていません。
(log (* 31 60 24) 2)
=> 15.446
したがって、これらの値を16ビットまたは2バイトに合わせることができます。これが良い考えであるかどうかは、まったく別の問題です。
ビットフィールドを使用し、コンパイラ/プラットフォーム固有のプラグマを使用して、それをタイトに保つことができます。
typedef struct packed_time_t {
unsigned int month : 4;
unsigned int date : 5;
unsigned int hour : 5;
unsigned int minute : 6;
} packed_time_t;
しかし、本当にこれが必要ですか?標準時の関数で十分ではないでしょうか?ビットフィールドは、アーキテクチャ、パディングなどによって異なります...ポータブル構造ではありません。
あなたが説明するユースケースでは、(31日の範囲の時間の分分解能)16ビットの分カウンターを使用します。このデータを(ディスク、ネットワークに)シリアル化する場合は、可変長整数エンコーディングを使用して、小さな値のバイトを節約できます。
代替手段を提供するだけです:
次に、タイムスタンプを最後のメッセージのタイムスタンプからのオフセットとして保存できます。
この場合、メッセージ間の最大分数を保持するのに十分なビットのみが必要です。たとえば、最大 255 分間隔でメッセージを送信する場合、1 バイトで十分です。
ただし、同期のために、最初のメッセージのペイロードに絶対タイムスタンプを含める必要がある場合があることに注意してください。
[これが良い解決策だと言っているのではありません - かなり壊れやすく、多くの仮定を立てています - 単なる代替案です]
余分な HOUR 24 と MINUTE 60 を無視すると、31 x 24 x 60 = 44,640 の一意の時間値が可能です。2^15 = 32,768 < 44,640 < 65,536 = 2^16 したがって、これらの値を表すには少なくとも 16 ビット (2 バイト) が必要です。
毎回値にアクセスするためにモジュロ演算を実行したくない場合は、それぞれを独自のビット フィールドに格納する必要があります。DAY を格納するために 5 ビット、HOUR を格納するために 5 ビット、MINUTE を格納するために 6 ビットが必要ですが、それでも 2 バイトに収まります。
struct day_hour_minute {
unsigned char DAY:5;
unsigned char HOUR:5;
unsigned char MINUTE:6;
};
MONTH を含めると、一意の時間値が 12 倍になり、535,680 個の一意の値が得られます。これを格納するには、少なくとも 20 ビットが必要です (2^19 = 524,288 < 535,680 < 1,048,576 = 2^20)。バイト。
繰り返しになりますが、モジュロ演算を避けるために、MONTH には別のビット フィールドが必要です。これには 4 ビットしか必要ありません。
struct month_day_hour_minute {
unsigned char MONTH:4;
unsigned char DAY:5;
unsigned char HOUR:5;
unsigned char MINUTE:6;
unsigned char unused: 4;
};
ただし、これらの例の両方で、C はデータ構造がオンカットであることを好むことに注意してください。つまり、データ構造は (通常は) 4 または 8 バイトの倍数であるため、最小限必要なものを超えてデータ構造を埋め込む可能性があります。
たとえば、私のマシンでは、
#include <stdio.h>
struct day_hour_minute {
unsigned int DAY:5;
unsigned int HOUR:5;
unsigned int MINUTE:6;
};
struct month_day_hour_minute {
unsigned int MONTH:4;
unsigned int DAY:5;
unsigned int HOUR:5;
unsigned int MINUTE:6;
unsigned int unused: 4;
};
#define DI( i ) printf( #i " = %d\n", i )
int main(void) {
DI( sizeof(struct day_hour_minute) );
DI( sizeof(struct month_day_hour_minute) );
return 0;
}
プリント:
sizeof(struct day_hour_minute) = 4
sizeof(struct month_day_hour_minute) = 4
一般性を失うことなくこれを単純化するには、
日 (0 ~ 30)、時 (0 ~ 23)、分 (0 ~ 59)
encoding = Day + (Hour + (Minute)*24)*31
Day = encoding %31
Hour = (encoding / 31) % 24
Minute = (encoding / 31) / 24
エンコーディングの最大値は 44639 で、16 ビットよりわずかに小さい値です。
編集:ランピオンは基本的に同じことを言った. これにより、ビットごとのインターリーブ表現よりも少ない最小表現が得られます。