如何将三个 4 位带符号整数(即 5 位)打包成一个 16 位整数?

How do you pack three 4 bit signed integers (so 5 bits) into a single 16 bit integer?

我想将 3 个带符号的 4 位整数(4 位数据,1 位符号位)打包成一个 16 位整数,但我不知道该怎么做或从哪里开始:(

我需要它用尽可能少的数据来表示 3D 网格中的位置(因为网格尺寸越大,它真的会加起来)。如果有帮助,我正在使用 GLM(OpenGL 数学库,所以我可以访问 glm::sign()

等函数

如果可能,请给我打包解压的代码。

谢谢

@JDługosz 如果您在支持该结构语法的平台上,那么答案很好。但是,您提到了 OpenGL。

这是一个可以在着色器中运行的版本。

基本上,以与平台无关的方式测试符号,为此设置一个位(1 表示负数,0 表示正数),添加要打包的 int 的低四位,然后将结果移动五位以为下一个值腾出空间。

由于您处理的是从 -15 到 +15 的值,您可以稍微简化一下。而不是检查符号,只需向值添加一个常量以强制它为正。 (不过,我建议在打包端添加一个 assert 以确保输入值实际上适合 4 位。)解包时,减去该常量。

TL;DR: 将你的输入转换为正整数,抓取低 5 位,然后 mask/shift/add.

int pack3 (int a, int b, int c)
{
    a = (a + 16) & 0x1F;
    b = (b + 16) & 0x1F;
    c = (c + 16) & 0x1F;

    return (a << 10) | (b << 5) | c;
}

void unpack3 (int p, int &a, int &b, int &c)
{ 
  // The 3 mask & subtraction ops could be done in one step on P, but
  // I left them separate here for something resembling clarity. 
  c = (p & 0x1f) - 16;
  b = ((p >> 5) & 0x1f) - 16;
  a = ((p >> 10) & 0x1f) - 16;
}

对于着色器实现,unpack3() 需要将 & 引用替换为 inout 或适用于您的着色器模型和语言的等效项。

See it working with a test driver here.

struct s {
   int16_t x : 5;
   int16_t y : 5;
   int16_t z : 5;
};
static_assert (sizeof(s) == sizeof(int16_t));

打包和解包是自动的:就像其他任何东西一样使用它 struct。例如。给定 s val;val.x=-17; 打包和 foo(val.z); 解包。

当您想将东西塞在一起以便在程序本身内使用时,这很好,但不适用于文件格式等文档交换,因为(如评论所述)它如何打包是实现定义的。

此外,static_assert 将确保它确实按照您的意图进行,所有字段仅使用一个词。我听说过的所有用于传统平台的编译器都会按预期打包。