バイナリの「圧縮」は、加重和の問題として一般化できます。そのための興味深い手法がいくつかあります。
- X mod (255) は、本質的にすべての独立した 8 ビット数の合計を意味します。
X mod 254 は、1 mod 254 = 1、256 mod 254 = 2、256*256 mod 254 = 2*2 = 4 などであるため、各桁を 2 倍の重みで合計することを意味します。
エンコードがビッグ エンディアンの場合、*(unsigned long long)array % 254 は加重合計を生成します (切り捨てられた範囲は 0..253)。次に、重み 2 の値を削除して手動で追加すると、正しい結果が得られます。
uint64_t a = *(uint64_t *)array; return (a & ~256) % 254 + ((a>>9) & 2);
重みを取得する他のメカニズムは、各 2 進数に 255 を事前に乗算し、正しいビットをマスクすることです。
uint64_t a = (*(uint64_t *)array * 255) & 0x0102040810204080ULL; // little endian
uint64_t a = (*(uint64_t *)array * 255) & 0x8040201008040201ULL; // big endian
どちらの場合も、255 の残りを取ることができます (重み 1 で修正します)。
return (a & 0x00ffffffffffffff) % 255 + (a>>56); // little endian, or
return (a & ~1) % 255 + (a&1);
懐疑的な心のために: 私は実際に係数バージョンを x64 での反復より (わずかに) 高速になるようにプロファイルしました。
JasonD の回答から続けると、並列ビット選択を繰り返し利用できます。しかし、最初に方程式を完全な形式で表現すると、累積を使用した反復アプローチによって作成された人為的な依存関係をコンパイラーが削除するのに役立ちます。
ret = ((a[0]<<7) | (a[1]<<6) | (a[2]<<5) | (a[3]<<4) |
(a[4]<<3) | (a[5]<<2) | (a[6]<<1) | (a[7]<<0));
対。
HI=*(uint32_t)array, LO=*(uint32_t)&array[4];
LO |= (HI<<4); // The HI dword has a weight 16 relative to Lo bytes
LO |= (LO>>14); // High word has 4x weight compared to low word
LO |= (LO>>9); // high byte has 2x weight compared to lower byte
return LO & 255;
もう 1 つの興味深い手法は、crc32 を圧縮関数として利用することです。結果が LookUpTable[crc32(array) & 255]; となるだけです。256 の異なる配列のこの特定の小さなサブセットとの衝突がないためです。ただし、それを適用するには、移植性の低い道をすでに選択しており、SSE 組み込み関数を使用することになる可能性があります。