【问题标题】:Check specific bit of QByteArray检查 QByteArray 的特定位
【发布时间】:2017-10-09 12:37:59
【问题描述】:

我有一个 8 字节的 QByteArray,我需要检查该数组中的特定位,但不是每次都检查相同的位。它可以是构成该 8 字节数组的 64 位中的任何一个。性能优先!

我当前的方法首先从该数组中获取一个特定字节,然后获取一个特定的半字节(或半字节),然后使用QByteArray::number(x, 2) 转换为具有二进制表示的另一个QByteArray,最后我检查该位.这很糟糕,我想要一个更好的方法。

我选择将其加载到 QBitArray 中,这样我就可以快速轻松地检索特定位。我假设它在内存中的表示与QByteArrayquint64 相同,因此可以接受转换,但不允许转换。

如何快速检查QByteArray 中的特定位(0 到 63)是 1 还是 0?

【问题讨论】:

  • 请将您当前的代码添加到问题中。我想知道为什么您不使用按位运算符来知道位的值。 stackoverflow.com/a/523737/2266412
  • 因为它接受 int 我正在寻找原始的 QByteArray 位检查。我首先必须找到该位所在的特定字节,转换为 int,然后使用该方法。字节位置和位位置需要逻辑才能确定。性能是一个问题,所以我想要更直接和更快的东西。我解释了我当前的代码,我根本不想使用它。它很乱,所以我认为发布它根本没有任何好处。
  • 您所说的“逻辑”在现代 CPU 上是微不足道的。您真的应该前往godbolt.org 亲自看看。
  • unsigned long long bit=reinterpret_cast<unsigned long long*>array.constData() & (1<<bitToCheck); 其中bitToCheck 是从 0 开始的
  • @PaulBentley 如果您所做的所有三个假设都是正确的,那么这将起作用:字节顺序、位顺序和unsigned long long 的大小。你正确的机会大约是四分之一。

标签: c++ qt bit qbytearray


【解决方案1】:

我猜你需要做的就是遍历比特。

1) 检查你想要的位

if(j+(i*CHAR_BIT) == (bit-1)) 

2) 如果传递的“位”不适合特定字节,则跳过不需要的字节

(((i+1)*CHAR_BIT) < (bit-1))

3) 如果当前位位置大于传递的位,则跳过所有其他字节。

((j+(i*CHAR_BIT)) > (bit-1))

以下是完整的解决方案。

quint8 checkBit(QByteArray bytes, int bit) {
    quint8 result=0;
    for(int i = 0; i < bytes.count(); ++i) {
        for (int j = 0; j < CHAR_BIT; ++j) {
            if(j+(i*CHAR_BIT) == (bit-1)) {
                result = (bytes.at(i) & (1 << (7-j))) ? 1 : 0;
                break;
            }
            else if(((i+1)*CHAR_BIT) < (bit-1) || ((j+(i*CHAR_BIT)) > (bit-1))) { // out of range skips
                break;
            }
        }
    }

    return result;
}

注意:由于位置从 0 开始,所以当你通过 bit .. 说 8 实际上是 8-1 = 7 : [0,1,2,3,4,5,6,7]

【讨论】:

    【解决方案2】:

    QBitArray 并非设计为可转换为其他任何东西;它的内部表示确实是内部的。

    唉,位检查相当容易。现代架构使用桶式移位器,因此移位很便宜。

    有几种可能的位到字节映射。让我们涵盖所有这些:

            byte 0     byte 1       byte n-1   byte n
    LL - [01234567] [89ABCDEF] ...
    LB - [76543210] [FEDCBA98] ...
    BL -                       ... [89ABCDEF] [01234567]
    BB -                       ... [FEDCBA98] [76543210]
    

    因此:

    enum class BitMapping { LL, LB, BL, BB };
    
    bool getBit1(const QByteArray & arr, int bit, BitMapping m) {
      Q_ASSERT(arr.size() >= 8);
      auto byte = (m == BitMapping::LL || m == BitMapping::LB) ? 
                  bit/8 : (7 - bit/8);
      bit = (m == BitMapping::LB || m == BitMapping::BB) ?
            (bit%8) : (7 - (bit%8));
      return arr.at(byte) & (1<<bit);
    }
    

    如果我们假设平台对 64 位整数有合理的支持,我们可以利用这些:

    bool getBit2(const QByteArray & arr, int bit, BitMapping m) {
       Q_ASSERT(arr.size() >= 8);
       auto value = *reinterpret_cast<const quint64 *>(arr.data());
       if (m == BitMapping::LL || m == BitMapping::BL)
          bit = (bit & 0x38) + 7 - (bit & 0x07); // reorder bits
       if ((Q_BYTE_ORDER == Q_LITTLE_ENDIAN && (m == BitMapping::BL || m == BitMapping::BB)) ||
           (Q_BYTE_ORDER == Q_BIG_ENDIAN && (m == BitMapping::LL || m == BitMapping::LB)))
          bit = (bit & 0x07) + 0x38 - (bit & 0x38); // reorder bytes
       return value & (1<<bit);
    }
    

    任何体面的编译器都会在专门化时内联上述任一实现,例如

    bool getBit(const QByteArray & arr, int bit) {
      return getBit2(arr, bit, BitMapping::LB);
    }
    

    您也可以针对 LB 案例手动对其进行专门化:

    bool getBit1(const QByteArray & arr, int bit) {
      Q_ASSERT(arr.size() >= 8);
      auto byte = bit/8;
      bit = bit%8;
      return arr.at(byte) & (1<<bit);
    }
    
    bool getBit2(const QByteArray & arr, int bit) {
       Q_ASSERT(arr.size() >= 8);
       auto value = *reinterpret_cast<const quint64 *>(arr.data());
       if (Q_BYTE_ORDER == Q_BIG_ENDIAN)
          bit = (bit & 0x07) + 0x38 - (bit & 0x38); // reorder bytes
       return value & (1<<bit);
    }
    

    请注意,Q_BYTE_ORDER 检查是编译时常量,不会产生运行时开销。

    getBit1getBit2 可移植到Qt 运行的所有平台,getBit2 生成的代码比getBit1 好一点。在 x86-64 上,来自getBit2 的位旋转代码相当于 5 条指令:

    mov    $0x1,%eax
    shl    %cl,%eax
    cltq   
    test   %rax,(%rdi)
    setne  %al
    retq   
    

    【讨论】:

    • 我对 LL、LB 等代表什么感到困惑?你能澄清一下吗?
    • 它们代表字节数组中的位顺序。有四种可能的顺序。它们以图形方式说明。什么不清楚?
    • 嗯,首先,L 和 B 代表什么。我假设是 Little 和 Big,但当我注意到 LL 和 LB 中的前 8 位不一样时,我感到很困惑。
    • @mrg95 它们不一样,因为第二个字母不一样 :) 代表什么并不重要,将它们视为任意标识符。您仍然必须知道在对字节数组进行编码时使用了四种位顺序中的哪一种,并且您必须选择正确的一种来提取数据。您可以以最适合您的方式命名这四个映射。取而代之的是 LL,LB,BL,BB 你可以有 Mapping1,Mapping2,Mapping3,Mapping4
    • 好吧,假设我只想使用 LB,有没有一种简化的方法呢?这似乎过于复杂了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-07
    • 1970-01-01
    • 2015-08-19
    • 1970-01-01
    • 1970-01-01
    • 2016-01-07
    • 1970-01-01
    相关资源
    最近更新 更多