OpenCL C中浮点向量的"共享指数"表示

lee*_*mes 5 c floating-point gpgpu bit-manipulation opencl

在OpenCL中,我想使用"共享指数"表示存储矢量(3D)以进行紧凑存储.通常,如果存储3D浮点矢量,则只需存储3个单独的浮点值(或正确对齐时为4).这需要12(16)字节存储以实现单精度,如果不需要这种精度,可以使用"半"精度浮点数并将其缩小到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浮点数的位表示来规避浮点ALU:

  • 使用uchar4为代表的类型.将x,y,z mantisssa存储在x,y,z中uchar4.(w & 0x1F)对于共享指数和三个更高有效位(w >> 5) & 1,w分量被分成5个较低有效位,(w >> 6) & 1并且(w >> 7) & 1分别是x,y和z的符号.
  • 注意,指数被"偏置"16,即存储值16表示所表示的数字达到(不包括)1.0,存储值19表示最多(不包括)8.0的值,依此类推.
  • float3可以使用以下代码将此表示"解包"到a中:

    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;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

我已经在ideone上在C++中使用了等效的C++实现.测试用例示出了从过渡exp = 3exp 4(与16的偏置,这是分别编码为19和20)通过围绕编码数字8.0.

这种实现似乎一见钟情.但:

  • 有一些我没有覆盖的角落情况,特别是指数上下溢.
  • 我不想使用浮点数学函数,log2因为它们很慢.

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

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

Dit*_*ter 1

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