C++ 如何对任意大小的内存实现按位的3态位运算符,同时最大限度地提高大小效率?

C++ 如何对任意大小的内存实现按位的3态位运算符,同时最大限度地提高大小效率?,c++,bit-manipulation,C++,Bit Manipulation,我可以对每3个状态位使用2个位来实现它,[00-第一位,10-第二位,11\01-第三位],但是当第二位被启用时,第一位就没用了。理论上,有一种实现在大小上比这种方法(我提到的2位)高出37%。(这是1-log3(2)) 我已经尝试过的代码: #define uint unsigned int uint set( uint x, uint place, uint value ) { double result = ( double )x; result /= pow( 3, p

我可以对每3个状态位使用2个位来实现它,[00-第一位,10-第二位,11\01-第三位],但是当第二位被启用时,第一位就没用了。理论上,有一种实现在大小上比这种方法(我提到的2位)高出37%。(这是
1-log3(2)

我已经尝试过的代码:

#define uint unsigned int

uint set( uint x, uint place, uint value ) {
    double result = ( double )x;
    result /= pow( 3, place );
    result += value - ( ( uint )result ) % 3;
    return result * pow( 3, place );
}
uint get( uint x, uint place ) {
    return ( ( uint )( ( ( double )x ) / pow( 3, place ) ) ) % 3;
}

int main( ) {
    uint s = 0;
    for ( int i = 0; i < 20; ++i )
        s = set( s, i, i % 3 );
    for ( int i = 0; i < 20; ++i )
        printf( "get( s, %d ) -> %u\n", i, get( s, i ) );
}
这种方法可以节省20%的体积。(
1-32/40
-使用我提到的第一种方法需要40位)理论上,当容量增加时,有效性也会增加。(当然接近37%)

如何对任何大小的数据实现类似的3状态按位方法,并最大限度地提高大小的有效性?如果我将数据用作
uint
s的数组并在其上使用此方法,我将只获得20%的有效性。(如果数据的大小不乘以4,则更低)

注意:我唯一需要的是尺寸有效性,我不关心速度性能。(除非您选择使用
biginger
而不是
uint

log32
与此无关

表示三值单位的最大可能效率为每单位
log23
位,而每单位2位的压缩为
(2-log23))/2,约为20.75%。所以20%是很好的

您不应该使用
pow
进行整数幂运算;除了速度慢之外,它有时会被1ULP关闭,这足以使它在强制为整数时被1关闭。但也不需要所有这些工作;您可以将五个3状态值压缩为一个字节(
35=243<256
),并且构建一个包含256个条目的查找表非常简单,每个可能的字节值对应一个条目

使用LUT,可以从大向量中提取3状态值:

/* All error checking omitted */
uint8_t LUT[243][5] = { {0,0,0,0,0}, {1,0,0,0,0}, ... };
uint8_t extract(const uint8_t* data, int offset) {
  return LUT[data[offset/5]][offset%5];
}
顺便说一句,如果1215字节的查找表被认为是“大的”(考虑到1GB的数据向量,这似乎很奇怪),那么将其压缩4倍就很容易了,尽管这会使表的构造复杂化

/* All error checking omitted */
uint8_t LUT[] = { /* Left as an exercise */ };
uint8_t extract(const uint8_t* data, unsigned offset) {
  unsigned index = data[offset/5] * 5 + offset % 5;
  return (LUT[index / 4] >> (2 * (index % 4))) & 3;
}

除了rici的回答之外,我还想发布我所做的代码,这也会有所帮助:(简化的一个)


我重新实现了
pow
方法,而不是大的查找表,它更安全、更快,还添加了
set
函数。

我认为,位篡改和pow(浮点)不是一个好的选择。(老实说,你需要2位,浪费一点信息)@DieterLücking我知道,但对于1GB的数据来说,这是一种浪费。(我的问题是如何以最有效的方式缩小尺寸)这样做有点错误,但它是有效的。(如果我可以做任何类似于长数据的事情,比如选择正确的位进行操作等等),您可以解释第二个代码。也花了一点时间来理解第一个。
/* All error checking omitted */
uint8_t LUT[] = { /* Left as an exercise */ };
uint8_t extract(const uint8_t* data, unsigned offset) {
  unsigned index = data[offset/5] * 5 + offset % 5;
  return (LUT[index / 4] >> (2 * (index % 4))) & 3;
}
uint8_t ft[ 5 ] = { 1, 3, 3 * 3, 3 * 3 * 3, 3 * 3 * 3 * 3 };
void set( uint8_t *data, int offset, int value ) {
    uint8_t t1 = data[ offset / 5 ], t2 = ft[ offset % 5 ], u8 = t1 / t2;
    u8 += value - u8 % 3;
    data[ offset / 5 ] = t1 + ( u8 - t1 / t2 )*t2;
}
uint8_t get( uint8_t *data, int offset ) {
    return data[ offset / 5 ] / ft[ offset % 5 ] % 3;
}