【问题标题】:Pack/unpack short into int将 short 打包/解包为 int
【发布时间】:2012-08-31 17:23:04
【问题描述】:

我想将两个带符号的 16 位整数打包/解包为一个 32 位整数。但是,我并没有让它完全发挥作用。

关于我可能做错了什么有什么想法吗?

template <typename T>
int read_s16(T& arr, int idx) restrict(amp)
{
    return static_cast<int>((arr[idx/2] >> ((idx % 2) * 16)) << 16) >> 16;
}

template<typename T>
void write_s16(T& arr, int idx, int val) restrict(amp)
{
    // NOTE: arr is zero initialized
    concurrency::atomic_fetch_or(&arr[idx/2], (static_cast<unsigned int>(val) & 0xFFFF) << ((idx % 2) * 16));
}

函数返回/参数必须是我定义的。 lohi 是从不同的线程写入的(因此是 atomic_or),读取必须返回单个 32 位值。

目标平台不支持 16 位整数运算。

例子:

array<int> ar(1); // Container

write_s16(ar, 0, -16);
write_s16(ar, 1, 5);

assert(read_s16(ar, 0) == -16);
assert(read_s16(ar, 1) == 5);

【问题讨论】:

  • 你能举一些输入和输出的例子吗?
  • 我内心的某些东西反对将数据同时打包到单个 int...
  • Kerrek SB:代码在 GPU 上运行。
  • 您只是想存储它们并将它们传递给其他人吗?你如何处理这些迹象?你能把它放进一个 uint16_t[2] 吗?从您的问题看来,您只是将它们打包,所以我建议使用未签名的数据。
  • 数据是有符号的,这让这有点复杂,它必须被放入一个无符号的 int 或 int 数组中。

标签: c++ c cuda bit-manipulation c++-amp


【解决方案1】:

看起来太复杂了,里面还有奇怪的操作。

通常,您只需这样做:

int32_t Pack(int16_t a, int16_t b)
{
   return (int32_t)((((uint32_t)a)<<16)+(uint32_t)b);
}

int16_t UnpackA(int32_t x)
{
   return (int16_t)(((uint32_t)x)>>16);
}

int16_t UnpackB(int32_t x)
{
   return (int16_t)(((uint32_t)x)&0xffff);
}

请注意,我使用具有显式位大小的类型来说明发生了什么。我还冒昧地假设您想要一个“整数”,而不是“无符号整数”。

【讨论】:

    【解决方案2】:

    C++ AMP 中的这些原子操作也有以下限制:

    • 您不应混合原子和正常(非原子)读取和写入。 正常读取可能看不到原子写入相同的结果 内存位置。正常写入不应与原子写入混合 到同一个内存位置。如果您的程序不符合 这些标准将导致未定义的结果。
    • 原子 操作并不意味着任何形式的内存栅栏。原子操作 可能会重新排序。这与互锁的行为不同 C++ 中的操作。

    您似乎违反了其中的第一个。

    【讨论】:

    【解决方案3】:

    下面的代码在 MSVC 中对我来说很好用。如您所见,原则上与您的代码相同。

    问题可能是您忘记将数组的内容初始化为零吗?您的平台如何处理负数并转换为 unsigned int?

    template <typename T>
    int read_s16(T& arr, int idx)
    {
        return static_cast<int>((arr[idx/2] >> ((idx % 2) * 16)) << 16) >> 16;
    }
    
    template<typename T>
    void write_s16(T& arr, int idx, int val)
    {
        // NOTE: arr is zero initialized
        arr[idx/2] |= (static_cast<unsigned int>(val) & 0xFFFF) << ((idx % 2) * 16);
    }
    
    int main()
    {
        int ar[2] = { 0,0 }; // container
    
        write_s16<int [2]>(ar, 0, -16);
        write_s16<int [2]>(ar, 1, 5);
    
        assert(read_s16<int [2]>(ar, 0) == -16);
        assert(read_s16<int [2]>(ar, 1) == 5);
    
        return 0;
    }
    

    【讨论】:

    • 嗯,在 cpu 上运行时它也适用于我,但在 gpu 上坏了。可能是 GPU 处理右移符号的方式不同。
    【解决方案4】:

    试试这个包装:

    int_32 = (int16_1 & 0x0000FFFF) | (int16_2 & 0xFFFF0000);
    

    拆包:

    int16_MSB = (int_32 >> 16) & 0xFFFF;
    int16_LSB = int_32 & 0xFFFF;
    

    【讨论】:

      猜你喜欢
      • 2013-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-30
      • 2015-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多