gpt4 book ai didi

c++ - 从字节中获取位

转载 作者:可可西里 更新时间:2023-11-01 18:35:37 25 4
gpt4 key购买 nike

我有以下功能:

int GetGroup(unsigned bitResult, int iStartPos, int iNumOfBites)
{
return (bitResult >> (iStartPos + 1- iNumOfBites)) & ~(~0 << iNumOfBites);
}

该函数从一个字节返回一组位。
即如果 bitResult=102 (01100110)2, iStartPos=5, iNumOfBites=3
输出: 2 (10)2对于 iStartPos=7, iNumOfBites=4输出: 3 (0110)2我正在寻找更好的方式/“友好”来做到这一点,即使用 bitset或类似的东西。有什么建议吗?

最佳答案

(src >> start) & ((1UL << len)-1)     // or 1ULL << if you need a 64-bit mask

是表达 len 提取的一种方式位,从 start 开始. (在本例中, start 是您想要的范围的 LSB。您的函数需要 MSB 作为输入。)该表达式来自 Wikipedia's article on the x86 BMI1 instruction set extensions .

两种生产口罩的方法看起来都有风险,以防万一 len不过是字体的全宽。 (提取所有位的极端情况)。按类型的全宽度移动可以产生零或不变。 (它实际上调用了未定义的行为,但实际上如果编译器在编译时看不到它,就会发生这种情况。例如,x86 将移位计数屏蔽到 0-31 范围(对于 32 位移位)。使用 32 位整数:
  • 如果 1 << 32 产生 1,则 1-1 = 0,因此结果将为零。
  • 如果 ~0 << 32 产生 ~0,而不是 0,则掩码将为零。

  • 请记住 1<<lenlen 的未定义行为太大:不像把它写成 0x3ffffffffff或者别的什么,没有自动升级到 long long发生,所以 1 的类型事项。

    我认为从你的例子中你想要这些位 [iStartPos : iStartPos - iNumOfBites] ,其中位从零开始编号。

    我要在您的函数中更改的主要内容是函数和变量的命名,并添加注释。
  • bitResult是函数的输入;不要在其名称中使用“结果”。
  • iStartPos好的,但有点冗长
  • iNumOfBites计算机有位和字节。如果您要处理咬伤,您需要看医生(或牙医)。

  • 此外,返回类型应该是 unsigned .
    // extract bits [msb : msb-len] from input into the low bits of the result
    unsigned BitExtract(unsigned input, int msb, int len)
    {
    return (input >> (msb-len + 1)) & ~(~0 << len);
    }

    如果您的 start-position 参数是 lsb 而不是 msb,则表达式会更简单,并且代码会更小更快(除非这会给调用者带来额外的工作)。以 LSB 作为参数,BitExtract 是 7 条指令,如果是 MSB(在 x86-64、gcc 5.2 上)则是 9 条指令。

    还有一个执行此操作的机器指令(由 Intel Haswell 和 AMD Piledriver 引入)。通过使用它,您将获得更小、更快的代码。它还使用 LSB、len 位置约定,而不是 MSB,因此您可以使用 LSB 作为参数获得更短的代码。

    Intel CPU 只知道需要先将立即数加载到寄存器中的版本,因此当这些值是编译时常量时,与简单的移位和屏蔽相比,它并没有节省多少。 e.g. see this post about using it or pextr for RGB32 -> RGB16 .当然,如果 start 和 len 都是编译时常量,则参数是所需范围的 MSB 还是 LSB 并不重要。

    只有AMD实现了 bextr的版本可以将控制掩码作为立即常量,但不幸的是,gcc 5.2 似乎没有将立即版本用于使用内在函数的代码(即使使用 -march=bdver2(即推土机 v2 又名打桩机)。(它将 generate bextr with an immediate argument on its own in some cases-march=bdver2 。)

    tested it out on godbolt查看使用或不使用 bextr 会得到什么样的代码。
    #include <immintrin.h>
    // Intel ICC uses different intrinsics for bextr

    // extract bits [msb : msb-len] from input into the low bits of the result
    unsigned BitExtract(unsigned input, int msb, int len)
    {
    #ifdef __BMI__ // probably also need to check for __GNUC__
    return __builtin_ia32_bextr_u32(input, (len<<8) | (msb-len+1) );
    #else
    return (input >> (msb-len + 1)) & ~(~0 << len);
    #endif
    }

    实现 movzx 需要额外的指令( (msb-len+1)&0xff)安全检查以避免起始字节溢出到长度字节中。我省略了它,因为要求 0-31 范围之外的起始位是无稽之谈,更不用说 0-255 范围了。既然它不会崩溃,就返回一些其他的废话结果,没有多大意义。

    不管怎样, bext保存了很多指令(如果 BMI2 shlx/ shrx 也不可用! -march=native 在 Godbolt 上是 Haswell,因此也包括 BMI2。)

    但是 bextr在 Intel CPU 上解码为 2 uops ( http://agner.org/optimize/ ),因此与 shrx 相比,它根本不是很有用/ and ,除了节省一些代码大小。 pext实际上对吞吐量更好(1 uop/3c 延迟),即使它是一种更强大的指令。但是,延迟更糟。和 AMD CPU 运行 pext很慢,但是 bextr作为一个单一的 uop。

    关于c++ - 从字节中获取位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33554553/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com