从32位数据类型有效地删除2位

时间:2018-12-21 08:08:03

标签: c++ algorithm bit-manipulation

假设您具有32位数据类型:

// The letters are just to identify the position of each bit later in the post
abcdefgh ijklmnop qrstuvwx yzABCDEF

我正在寻找在某些位置“丢弃”位的最有效方法,其中丢弃意味着“移除”给定的位,然后移动随后的位以填充其位置。

示例:假设我要删除位“ a”和“ q”。然后结果应该是:

bcdefghi jklmnopr stuvwxyz ABCDEF00

00bcdefg hijklmno prstuvwx yzABCDEF

任何一个结果都是可以接受的。

在我的特定情况下,我还可以施加以下约束:

  • 在我的情况下,下降的位置是静态的;即,我将始终需要准确删除第1位和第16位(“ a”和“ q”)
  • 要丢弃的位(“ a”和“ q”)始终为0
  • 最后填充数据的位(操作后在左侧或右侧的“ 00”)无关紧要-即,它们实际上是0还是1无关紧要

目前,我正在使用这种方法(伪代码):

// called with number = abcdefgh ijklmnop qrstuvwx yzABCDEF
auto drop_bits_1_16(unsigned int number)
{
    number = number << 1; // number becomes: bcdefghi jklmnopq rstuvwxy zABCDEF0
    unsigned number1 = number & 0xFFFE0000;  // number1 comes: bcdefghi jklmnop0 00000000 00000000

    unsigned number2 = number & 0x0000FFFF; // number2 becomes: 00000000 00000000 rstuvwxy zABCDEF0
    number2 = number2 << 1;  // number2 becomes: 00000000 0000000r stuvwxyz ABCDEF00

    return number1 | number2;  // returns bcdefghi jklmnopr stuvwxyz ABCDEF00
}

但是我想知道是否还有更聪明/更有效的出路?

5 个答案:

答案 0 :(得分:5)

向右打包比向左打包要容易一些,因为只需要移动15位,而不是两次15。我看不到如何可以使屏蔽无效,所以

((number & 0x7FFF0000) >> 1) | (number & 0x00007FFF)

这不需要丢弃的位为零。有四个按位运算,减少难度。


有三种方法可以实现!

添加15个低位,将它们左移一位(乘以2),然后将整个整数右移。

(number + (number & 0x7FFF)) >> 1

警告:位15必须为零。

也许以下表达式将为编译器提供一些选项,以更好地生成代码:

(number + (unsigned short)number) >> 1

我应该补充说其他最终布局也是可能的吗?

(number + (unsigned short)number) << 1

答案 1 :(得分:1)

您可以用另一种方式做

auto drop_bits_1_16(unsigned int number)
{
    unsigned number1 = number & 0x7FFF0000; // number1 becomes: 0bcdefgh ijklmnop 00000000 00000000
    unsigned number2 = number & 0x00007FFF; // number2 becomes: 00000000 00000000 0rstuvwx yzABCDEF
    number1 = number1 >> 1; // number1 becomes: 00bcdefg hijklmno p0000000 00000000

    return number1 | number2;  // returns 00bcdefg hijklmno prstuvwx yzABCDEF
}

它更短,并且具有可读性更好的优点:很清楚从位掩码中丢弃了哪些位。

您还可以将其设置为单线:

auto drop_bits_1_16(unsigned int number)
{
    return ((number & 0x7FFF0000) >> 1) | (number & 0x00007FFF);
    // Or, relying on operator precedence:
    // return (number & 0x7FFF0000) >> 1 | number & 0x00007FFF;
}

可以说比哪一种解决方案更清晰:

auto drop_bits_1_16(unsigned int number)
{
    return ((number << 1) & 0xFFFE0000) | (((number << 1) & 0x0000FFFF) << 1);
    // Or, relying on operator precedence:
    // return number << 1 & 0xFFFE0000 | (number << 1 & 0x0000FFFF) << 1;
}

或者,按照@greybeard的建议(但仍然可以说不清楚):

auto drop_bits_1_16(unsigned int number)
{
    return ((number << 1) & 0xFFFE0000) | ((number << 2) & 0x0001FFFC);
    // Or, relying on operator precedence:
    // return number << 1 & 0xFFFE0000 | number << 2 & 0x0001FFFC;
}

答案 2 :(得分:1)

我想出了这个通用解决方案。从我的角度来看,需要分为三个部分。

删除第3位和第20位。 (从零开始)

3
1            v                     v  0
hhhh hhhh hhhx mmmm mmmm mmmm mmmm xlll

您需要掩盖低点。中高部分,然后将它们压在一起。

template <size_t low, size_t hi> unsigned int remove_bits(unsigned int all)
{
    // static constants - my compiler pre-computes them.  They are the masks for
    // hhhh, mmmm and llll
    static const unsigned int lowMask = 0x7fffffff >> (31 - low);
    static const unsigned int middleMask = ((0xfffffffe << low) & (0x7fffffff >> (31 - hi) ));
    static const unsigned int highMask = 0xfffffffe << hi;

    // find the values in hhhh, mmmm, and llll
    unsigned int resLow = (all & lowMask);
    unsigned int resMiddle = (all & middleMask);
    unsigned int resHigh = (all & highMask);

    //////////////////////////////////////
    // combine the parts, shifted to the lower end.

    return resLow | resMiddle >> 1 | resHigh >> 2;
} 

使用类似

的电话
printf("Question q %x\n", remove_bits<1, 31>(0x12345678));

答案 3 :(得分:0)

我认为没有什么比指定的实现更简单的了:

unsigned int drop_bits_16_32(unsigned int number)
{
    number <<= 1;
    unsigned int high = number & 0xFFFE0000;
    unsigned int low = (number & 0x0000FFFF) << 1;

    return high | low;
}

答案 4 :(得分:0)

您可以使用4条指令(而不是5条指令)在左侧放位:

unsigned f1(unsigned x) {
    x <<= 1;
    return x + ((signed) (x << 15) >> 15);
}

请注意,带符号的右移复制了要删除的位,以便在添加中抵消。