假设您具有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
任何一个结果都是可以接受的。
在我的特定情况下,我还可以施加以下约束:
目前,我正在使用这种方法(伪代码):
// 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
}
但是我想知道是否还有更聪明/更有效的出路?
答案 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);
}
请注意,带符号的右移复制了要删除的位,以便在添加中抵消。