二进制“压缩”可以概括为加权和问题——为此有一些有趣的技术。
- X mod (255) 本质上是对所有独立的 8 位数字求和。
-
X mod 254 表示将每个数字加倍权重,因为 1 mod 254 = 1、256 mod 254 = 2、256*256 mod 254 = 2*2 = 4 等等。
如果编码是大端,那么 *(unsigned long long)array % 254 将产生加权和(截断范围为 0..253)。然后删除权重为 2 的值并手动添加将产生正确的结果:
uint64_t a = *(uint64_t *)array;
返回 (a & ~256) % 254 + ((a>>9) & 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;
一种更有趣的技术是利用 crc32 作为压缩函数;然后碰巧结果是 LookUpTable[crc32(array) & 255];因为与这个给定的 256 个不同数组的小子集没有冲突。然而,要应用它,人们已经选择了更不便携的道路,并且最终可能会使用 SSE 内在函数。