代码之家  ›  专栏  ›  技术社区  ›  Philip DiSarro

将4字节整数交错到8字节整数

  •  1
  • Philip DiSarro  · 技术社区  · 7 年前

    this research 但我所有的尝试都失败了。我正在处理的具体输入是: 0x12345678 0xdeadbeef ,我想要的结果是 0x12de34ad56be78ef . 这是我目前的工作:

    unsigned long interleave(uint32_t x, uint32_t y){
        uint64_t result = 0;
        int shift = 33;
    
        for(int i = 64; i > 0; i-=16){
            shift -= 8;
            //printf("%d\n", i);
            //printf("%d\n", shift);
            result |= (x & i) << shift;
            result |= (y & i) << (shift-1);
        }
    }
    

    但是,此函数不断返回 0xfffffffe 这是不正确的。我正在使用以下方法打印和验证这些值:

    printf("0x%x\n", z);
    

    uint32_t x = 0x12345678;
    uint32_t y = 0xdeadbeef;
    

    在这个主题上的任何帮助都将不胜感激,C语言对我来说是一门非常困难的语言,而位运算更是如此。

    4 回复  |  直到 7 年前
        1
  •  1
  •   bigwillydos    7 年前

    使用位移动和位操作(与端点无关):

    uint64_t interleave(uint32_t x, uint32_t y){
    
        uint64_t result = 0;
    
        for(uint8_t i = 0; i < 4; i ++){
            result |= ((x & (0xFFull << (8*i))) << (8*(i+1)));
            result |= ((y & (0xFFull << (8*i))) << (8*i));
        }
    
        return result;
    }
    

    带指针(依赖于端点):

    uint64_t interleave(uint32_t x, uint32_t y){
    
        uint64_t result = 0;
    
        uint8_t * x_ptr = (uint8_t *)&x;
        uint8_t * y_ptr = (uint8_t *)&y;
        uint8_t * r_ptr = (uint8_t *)&result;
    
        for(uint8_t i = 0; i < 4; i++){
            *(r_ptr++) = y_ptr[i];
            *(r_ptr++) = x_ptr[i];
        }
    
        return result;
    
    }
    

        2
  •  3
  •   user555045    7 年前

    这可以基于 interleaving bits

    以下是我的计划,以我惊人的手绘技巧为例:

    permutation

    // step 1, moving the top two bytes
    uint64_t a = (((uint64_t)x & 0xFFFF0000) << 16) | (x & 0xFFFF);
    // step 2, moving bytes 2 and 6
    a = ((a & 0x00FF000000FF0000) << 8) | (a & 0x000000FF000000FF);
    // same thing with y
    uint64_t b = (((uint64_t)y & 0xFFFF0000) << 16) | (y & 0xFFFF);
    b = ((b & 0x00FF000000FF0000) << 8) | (b & 0x000000FF000000FF);
    // merge them
    uint64_t result = (a << 8) | b;
    

    有人建议使用SSSE3 PSHUFB,它可以工作,但是有一条指令可以一次性执行字节交叉, punpcklbw . 所以我们真正需要做的就是将值输入和输出到向量寄存器,然后单指令就可以处理它了。

    未测试:

    uint64_t interleave(uint32_t x, uint32_t y) {
      __m128i xvec = _mm_cvtsi32_si128(x);
      __m128i yvec = _mm_cvtsi32_si128(y);
      __m128i interleaved = _mm_unpacklo_epi8(yvec, xvec);
      return _mm_cvtsi128_si64(interleaved);
    }
    
        3
  •  1
  •   M.M    7 年前

    你可以这样做:

    uint64_t interleave(uint32_t x, uint32_t y)
    {
         uint64_t z;
    
         unsigned char *a = (unsigned char *)&x;   // 1
         unsigned char *b = (unsigned char *)&y;   // 1
         unsigned char *c = (unsigned char *)&z;
    
         c[0] = a[0];
         c[1] = b[0];
         c[2] = a[1];
         c[3] = b[1];
         c[4] = a[2];
         c[5] = b[2];
         c[6] = a[3];
         c[7] = b[3];
    
         return z;
    }
    

    换乘站 a b 1 取决于订购要求。

    有移位的版本,其中 y 始终是输出的LSB,如示例中所示,为:

    uint64_t interleave(uint32_t x, uint32_t y)
    {
         return 
               (y & 0xFFull)
             | (x & 0xFFull)       << 8
             | (y & 0xFF00ull)     << 8
             | (x & 0xFF00ull)     << 16
             | (y & 0xFF0000ull)   << 16
             | (x & 0xFF0000ull)   << 24
             | (y & 0xFF000000ull) << 24
             | (x & 0xFF000000ull) << 32;
    }
    

    我试过的编译器似乎在优化这两个版本方面都做得不好,所以如果这是一个性能关键的情况,那么从注释中得到的内联汇编建议可能是一个不错的选择。

        4
  •  1
  •   0___________    7 年前

    #include <stdio.h>
    #include <stdint.h>
    #include <string.h>
    
    typedef union
    {
            uint64_t u64;
            struct 
            {
                union
                {
                    uint32_t a32;
                    uint8_t a8[4]
                };
                union
                {
                    uint32_t b32;
                    uint8_t b8[4]
                };
            };
            uint8_t u8[8];
    }data_64;
    
    uint64_t interleave(uint32_t a, uint32_t b)
    {
        data_64 in , out;
    
        in.a32 = a;
        in.b32 = b;
    
    
    
        for(size_t index = 0; index < sizeof(a); index ++)
        {
    
            out.u8[index * 2 + 1] = in.a8[index];
            out.u8[index * 2 ] = in.b8[index];
        }
        return out.u64;
    }
    
    
    int main(void)
    {
    
        printf("%llx\n", interleave(0x12345678U, 0xdeadbeefU)) ;
    }