【问题标题】:How to convert 8 17-bit integers into 17 8-bit integers efficiently如何高效地将 8 个 17 位整数转换为 17 个 8 位整数
【发布时间】:2011-10-12 18:47:10
【问题描述】:

好的,我有以下问题:我有一组 8 个(无符号)数字,它们都是 17 位的(也就是没有一个大于 131071)。由于 17 位数字很烦人(将它们保存在 32 位 int 中会浪费空间),我想将它们转换为 17 个 8 位数字,如下所示:

如果我有这 8 个 17 位整数:

[25409, 23885, 24721, 23159, 25409, 23885, 24721, 23159]

我会把它们变成一个基数为 2 的表示L

["00110001101000001", "00101110101001101", "00110000010010001", "00101101001110111", "00110001101000001", "00101110101001101", "00110000010010001", "00101101001110111"]

然后将其连接成一个大字符串:

"0011000110100000100101110101001101001100000100100010010110100111011100110001101000001001011101010011010011000001001000100101101001110111"

然后将其拆分为 17 个字符串,每个字符串有 8 个字符:

["00110001", "10100000", "10010111", "01010011", "01001100", "00010010", "00100101", "10100111", "01110011", "00011010", "00001001", "01110101", "00110100", "11000001", "00100010", "01011010", "01110111"]

最后,将二进制表示转换回整数

[49, 160, 151, 83, 76, 18, 37, 167, 115, 26, 9, 117, 52, 193, 34, 90, 119]

此方法有效,但效率不高,我正在寻找比这更有效的方法,最好用 C++ 编码,因为这是我正在使用的语言。我只是想不出任何更有效的方法,而且 17 位数字并不是很容易使用(使用 16 位数字会更好)。

提前感谢,xfbs

【问题讨论】:

  • 您在哪个平台上工作,担心将这些存储在 32 位整数中会浪费空间?
  • @Praetorian:大概是任何可用内存少于两倍数据大小的平台。
  • @Steve Jessop 好吧,它并不完全是两倍,只是多出了 88% 的空间 :-) 但是说真的,如果程序的其他部分期望这些数字是 17 位整数,而不是字节数组彼此相邻,OP 会将这些字节重新转换为 17 位表示。他是否使用所有格式的按摩代码来节省内存是有争议的。
  • 您担心效率的标准是什么?速度?记忆?关于磁盘序列化?我也对平台问题很好奇,这似乎是你想多了,如果它没有什么特别的。
  • 其实我并不太担心浪费空间,另一个原因是我可以轻松导出 8bit 整数:它们恰好有合适的大小将它们转换为字符,然后我可以将整个内容导出为字符串。

标签: c++ integer


【解决方案1】:

按原样存储每个数字的最低 16 位(即两个字节)。这留下了每个数字的最高有效位。由于有 8 个这样的数字,只需将这 8 个位组合成一个额外的字节。

这将需要与您的方法完全相同的内存量,但涉及的操作要少得多。

附:无论采用哪种存储方式,都应该使用位操作运算符(<<>>&| 等)来完成这项工作;不应涉及任何基于字符串的中间表示。

【讨论】:

  • 我已经考虑过这一点,但是我不会得到与我目前使用的方法相同的输出,而且由于向后兼容性,我真的不想更改它。
【解决方案2】:

看看std::bitset<N>。也许你可以把它们塞进去?

【讨论】:

  • 好吧,我也可以把它们塞进一个向量,但这有什么用呢? bitset 是否具有以不同形式(8bit int)导出数据的功能?
  • 这个答案的重点是完全重新设计您的程序,使其始终使用std::bitset,而不是完全重新打包。
  • @xfbs,您没有向我们提供详细信息,所以我无法回答您的问题。
  • @unkulunkulu 好吧,我的意思是我正在寻找 17 个 8 位整数,所以输出将是数字。但是,我可以在任何情况下使用 bitset,因为使用它可能比使用字符串更好(更有效,使用更少的内存)。
【解决方案3】:

有效吗?然后不要使用字符串转换、位域等。设法自己进行转变以实现这一目标。 (注意数组必须是unsigned,这样我们移位的时候就不会遇到问题了)。

uint32 A[8]; //Your input, unsigned int
ubyte B[17]; //Output, unsigned byte
B[0] = (ubyte)A[0];
B[1] = (ubyte)(A[0] >> 8);
B[2] = (ubyte)A[1];
B[3] = (ubyte)(A[1] >> 8);
.
:

对于最后一个,我们按照 ajx 所说的去做。我们取每个数字的最高有效位(将它们向右移动 16 位留下第 17 位)并通过将每个最高有效位从 0 向左移动到 7 来填充输出的位:

B[16] = (A[0] >> 16)  | ((A[1] >> 16) << 1) | ((A[2] >> 16) << 2) | ((A[3] >> 16) << 3) | ... | ((A[7] >> 16) << 7);

嗯,“高效”就是这样。还有其他更简单的方法。

【讨论】:

  • 谢谢!我从没想过使用移位操作...这听起来比将数字临时转换为字符串要快得多,而且它可能也更快,因为移位需要很少的 CPU 周期:D 谢谢,我试试这个
  • 嗯,仔细想想,可能有一点瑕疵:我需要担心字节序吗?
  • 字节序是关于如何将大于 1 字节的整数存储到内存中;但是您不必担心,因为您只是从源中读取“数字”(无论它们在内存中如何,0xff00 &gt;&gt; 8 在大端或小端机器上都会给出相同的结果)并一致地写出字节(即使你会输出 16 位字,故事也是一样的)
【解决方案4】:

虽然你说它们是 17 位数字,但它们必须存储在一个 32 位整数数组中,其中只使用较低的 17 位。您可以直接从第一个字节中提取两个字节(dst[0] = src[0] &gt;&gt; 9 是第一个,dst[1] = (src[0] &gt;&gt; 1) &amp; 0xff 是第二个);然后你将第一位“推”为第二位的第 18 位,这样

  dst[2] = (src[0] & 1) << 7 | src[1] >> 10;
  dst[3] = (src[1] >> 2) & 0xff;

如果你概括一下,你会看到这个“公式”可以应用

   dst[2*i] = src[i] >> (9+i) | (src[i-1] & BITS(i)) << (8-i);
   dst[2*i + 1] = (src[i] >> (i+1)) & 0xff;

最后一个:dst[16] = src[7] &amp; 0xff;

整个代码可能看起来像

  dst[0] = src[0] >> 9;
  dst[1] = (src[0] >> 1) & 0xff;

  for(i = 1; i < 8; i++)
  {
    dst[2*i] = src[i] >> (9+i) | (src[i-1] & BITS(i)) << (8-i);
    dst[2*i + 1] = (src[i] >> (i+1)) & 0xff;
  }
  dst[16] = src[7] & 0xff;

可能更好地分析循环,可以进行优化,以便我们不需要以特殊方式处理边界上的情况。 BITS 宏创建一个 N 位掩码,设置为 1(最低有效位)。类似的东西(检查是否有更好的方法,如果有的话)

#define BITS(I) (~((~0)<<(I)))

添加

这里我认为 src 是例如int32_t 和 dst int8_t 或类似的。

【讨论】:

  • 这仅在您有 8 个“17 位”整数且 int 大于 16 位时才有效;由于我们正在考虑掩码和位范围,您不必担心 &gt;&gt;&lt;&lt; 是算术或逻辑移位(我希望不会错!)。
【解决方案5】:

这是在 C 中,所以你可以使用 vector 代替。

#define srcLength 8
#define destLength 17
int src[srcLength] = { 25409, 23885, 24721, 23159, 25409, 23885, 24721, 23159 };
unsigned char dest[destLength] = { 0 };

int srcElement = 0;
int bits = 0;
int i = 0;
int j = 0;

do {
    while( bits >= srcLength ) {
        dest[i++] = srcElement >> (bits - srcLength);
        srcElement = srcElement & ((1 << bits) - 1);
        bits -= srcLength;
    }

    if( j < srcLength ) {
        srcElement <<= destLength;
        bits += destLength;
        srcElement |= src[j++];
    }
} while (bits > 0);

免责声明:如果您确实有 17 个整数(而不是 100000 组乘以 17),只要您的程序运行不慢,您就应该忘记这些优化。

【讨论】:

    【解决方案6】:

    我可能会这样做。当我进行处理时,我不想处理奇怪的类型。由于遗留问题,也许我需要将它们存储在一些时髦的格式中。硬编码的值可能应该基于 17 值,只是没有打扰。

    struct int_block {
        static const uint32 w = 17;
        static const uint32 m = 131071;
        int_block() : data(151, 0) {} // w * 8 + (sizeof(uint32) - w)
        uint32 get(size_t i) const {
            uint32 retval = *reinterpret_cast<const uint32 *>( &data[i*w] );
            retval &= m;
            return retval;
        }
        void set(size_t i, uint32 val) {
            uint32 prev = *reinterpret_cast<const uint32 *>( &data[i*w] );
            prev &= ~m;
            val |= prev;
            *reinterpret_cast<uint32 *>( &data[i*w] ) = val;
        }
        std::vector<char> data;
    };
    
    TEST(int_block_test) {
    
        int_block ib;
        for (uint32 i = 0; i < 8; i++)
            ib.set(i, i+25);
    
        for (uint32 i = 0; i < 8; i++)
            CHECK_EQUAL(i+25, ib.get(i));
    }
    

    你可以通过给它错误的值来打破它,但我会把它留给读者作为练习。 :))

    老实说,我认为你会更乐意将它们表示为 32 位整数并只编写转换函数。但我怀疑你无法控制它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-06
      • 2014-10-07
      • 2016-01-03
      • 2012-12-13
      • 1970-01-01
      • 2019-02-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多