c - "Shared Exponent"OpenCL C 中浮点 vector 的表示

标签 c floating-point opencl bit-manipulation gpgpu

在 OpenCL 中,我想使用“共享指数”表示来存储 vector (3D) 以进行紧凑存储。通常,如果您存储 3D 浮点 vector ,您只需存储 3 个单独的浮点值(或 4 个正确对齐时)。这需要 12 (16) 字节的单精度存储,如果您不需要这种精度,您可以使用 "half" precision float并将其缩小到 6 (8) 个字节。

当使用半精度和 3 个独立值时,内存看起来像这样(不考虑对齐):

  • x坐标:1位符号,5位指数,10位尾数
  • y坐标:1位符号,5位指数,10位尾数
  • z坐标:1位符号,5位指数,10位尾数

我想通过使用共享 指数将它缩小到 4 个字节,因为 OpenGL 在其内部纹理格式之一(“RGB9_E5”)中使用它。这意味着,绝对最大的分量决定了整数的指数是多少。然后该指数隐式地用于每个组件。诸如带有隐式“1”的“规范化”存储等技巧。在这种情况下,尾数前面不起作用。这样的表示是这样工作的(我们可以调整实际参数,所以这是一个例子):

  • x坐标:1位符号,8位尾数
  • y坐标:1位符号,8位尾数
  • z坐标:1位符号,8位尾数
  • 5 位共享指数

我想将其存储在 OpenCL uint 类型(32 位)或类似的东西(例如 uchar4)中。现在的问题是:

如何尽可能快地将此表示形式与 float3 相互转换?

我的想法是这样的,但我确信有一些“位黑客”技巧使用 IEEE float 的位表示来绕过浮点 ALU:

  • 使用uchar4作为代表类型。将 x、y、z 尾数存储在此 uchar4 的 x、y、z 分量中。 w 分量被分成 5 个较低有效位 (w & 0x1F) 用于共享指数和三个较高有效位 (w >> 5) & 1, (w >> 6) & 1(w >> 7) & 1 分别是 x、y 和 z 的符号。
  • 请注意,指数“偏向”16,即存储值为 16 表示表示的数字最大(不包括)1.0,存储值为 19 表示最大(不包括)8.0 等上。
  • 可以使用以下代码将此表示“解包”为 float3:

    float3 unpackCompactVector(uchar4 packed) {
        float exp = (float)(packed.w & 0x1F) - 16.0;
        float factor = exp2(exp) / 256.0;
        float x = (float)(packed.x) * factor * (packed.w & 0x20 ? -1.0 : 1.0);
        float y = (float)(packed.y) * factor * (packed.w & 0x40 ? -1.0 : 1.0);
        float z = (float)(packed.z) * factor * (packed.w & 0x80 ? -1.0 : 1.0);
        float3 result = { x, y, z };
        return result;
    }
    
  • 可以使用以下代码将 float3“打包”到此表示中:

    uchar4 packCompactVector(float3 vec) {
        float xAbs = abs(vec.x);   uchar xSign = vec.x < 0.0 ? 0x20 : 0;
        float yAbs = abs(vec.y);   uchar ySign = vec.y < 0.0 ? 0x40 : 0;
        float zAbs = abs(vec.z);   uchar zSign = vec.z < 0.0 ? 0x80 : 0;
        float maxAbs = max(max(xAbs, yAbs), zAbs);
        int exp = floor(log2(maxAbs)) + 1;
        float factor = exp2(exp);
        uchar xMant = floor(xAbs / factor * 256);
        uchar yMant = floor(yAbs / factor * 256);
        uchar zMant = floor(zAbs / factor * 256);
        uchar w = ((exp + 16) & 0x1F) + xSign + ySign + zSign;
        uchar4 result = { xMant, yMant, zMant, w };
        return result;
    }
    

我已经用 C++ 实现了等效的实现 online on ideone .测试用例显示了从 exp = 3exp 4 的转换(偏差为 16,这分别编码为 19 和 20)通过围绕 编码数字8.0

这个实现乍一看似乎可行。但是:

  • 有一些极端情况我没有涉及,特别是(指数的)溢出和下溢。
  • 我不想使用像 log2 这样的 float 学函数,因为它们很慢。

你能建议一个更好的方法来实现我的目标吗?

请注意,为此我只需要一个 OpenCL“设备 代码”,我不需要在主机 程序中的表示之间进行转换。但我添加了 C 标签,因为解决方案很可能独立于 OpenCL 语言功能(OpenCL 几乎是 C,它也使用 IEEE 754 float ,位操作工作相同,等等)。

最佳答案

如果您使用 CL/GL 互操作并将数据存储在 RGB9_E5 格式的 OpenGL 纹理中,并且如果您可以从该纹理创建 OpenCL 图像,则可以利用硬件纹理单元在读取时将其转换为 float4图片。这可能值得一试。

关于c - "Shared Exponent"OpenCL C 中浮点 vector 的表示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17138385/

相关文章:

c - "hello x"- 字符串比较

python - 如何确定小数是否可以完全表示为 Python float?

floating-point - "epsilon"真的能保证浮点计算中的任何东西吗?

c++ - C复制3维数组

c - x86_64 linux 上 C 程序中的内联汇编

c - 为什么此 C 代码片段中的 NULL 取消引用不会导致未定义的行为

c - 如何定义 float 组

c++ - Intel i5 处理器优于 ATI HD Radeon GPU?

OpenCL:通过划分GPU资源来并发内核执行

c++ - CPU 设备上的 OpenCL - 幕后发生了什么?