给定32位int,已知至少设置了2位,有没有办法有效地清除除2个最重要的设置位之外的所有位?即我想确保输出正好设置为2位。
如果输入保证只设置了2或3位,该怎么办?
示例:
0x2040 -> 0x2040
0x0300 -> 0x0300
0x0109 -> 0x0108
0x5040 -> 0x5000
基准测试结果:
代码:
QueryPerformanceFrequency(&freq);
/***********/
value = (base =2)|1;
QueryPerformanceCounter(&start);
for (l=0;l<A_LOT; l++)
{
//!!value calculation goes here
junk+=value; //use result to prevent optimizer removing it.
//advance to the next 2|3 bit word
if (value&0x80000000)
{ if (base&0x80000000)
{ base=6;
}
base*=2;
value=base|1;
}
else
{ value<<=1;
}
}
QueryPerformanceCounter(&end);
time = (end.QuadPart - start.QuadPart);
time /= freq.QuadPart;
printf("--------- name\n");
printf("%ld loops took %f sec (%f additional)\n",A_LOT, time, time-baseline);
printf("words /sec = %f Million\n",A_LOT/(time-baseline)/1.0e6);
在Core2Duo E7500@2.93 GHz上使用VS2005默认版本设置的结果:
--------- BASELINE
1000000 loops took 0.001630 sec
--------- sirgedas
1000000 loops took 0.002479 sec (0.000849 additional)
words /sec = 1178.074206 Million
--------- ashelly
1000000 loops took 0.004640 sec (0.003010 additional)
words /sec = 332.230369 Million
--------- mvds
1000000 loops took 0.005250 sec (0.003620 additional)
words /sec = 276.242030 Million
--------- spender
1000000 loops took 0.009594 sec (0.007964 additional)
words /sec = 125.566361 Million
--------- schnaader
1000000 loops took 0.025680 sec (0.024050 additional)
words /sec = 41.580158 Million
答案 0 :(得分:8)
如果输入保证恰好有2或3位,则可以非常快速地计算答案。我们利用表达式x&amp;(x-1)等于x且LSB被清除的事实。如果设置了2个或更少的位,则将该表达式两次应用于输入将产生0。如果设置了2位,我们返回原始输入。否则,我们返回原始输入并清除LSB。
以下是C ++中的代码:
// assumes a has exactly 2 or 3 bits set
int topTwoBitsOf( int a )
{
int b = a&(a-1); // b = a with LSB cleared
return b&(b-1) ? b : a; // check if clearing the LSB of b produces 0
}
如果你愿意,这可以写成一个令人困惑的单个表达式:
int topTwoBitsOf( int a )
{
return a&(a-1)&((a&(a-1))-1) ? a&(a-1) : a;
}
答案 1 :(得分:4)
我会在循环中创建一个蒙版。开始时,掩码为0.然后从MSB进入LSB并将掩码中的每个相应位设置为1,直到找到2个设置位。最后和这个掩码的值。
#include <stdio.h>
#include <stdlib.h>
int clear_bits(int value) {
unsigned int mask = 0;
unsigned int act_bit = 0x80000000;
unsigned int bit_set_count = 0;
do {
if ((value & act_bit) == act_bit) bit_set_count++;
mask = mask | act_bit;
act_bit >>= 1;
} while ((act_bit != 0) && (bit_set_count < 2));
return (value & mask);
}
int main() {
printf("0x2040 => %X\n", clear_bits(0x2040));
printf("0x0300 => %X\n", clear_bits(0x0300));
printf("0x0109 => %X\n", clear_bits(0x0109));
printf("0x5040 => %X\n", clear_bits(0x5040));
return 0;
}
这非常复杂,但每次使用超过32位的for循环应该更有效(并清除除2个最重要的设置之外的所有位)。无论如何,在使用之前一定要以不同的方式进行基准测试。
当然,如果内存不是问题,请使用像推荐的查找表方法 - 这会更快。
答案 2 :(得分:1)
延迟时间有多少可用内存?我建议一个查找表; - )
但严重的是:如果你要在100个数字上执行此操作,那么8位查找表给出2 msb,另一个8位查找表给出1 msb可能就是你所需要的。根据处理器的不同,这可能会超过计数位。
为了速度,我会创建一个将输入字节映射到
的查找表如果设置1或0位,则M(I)= 0
M(I)= B'否则,其中B'是B的值,设置为2 msb位。
你的32位int是4个输入字节I1 I2 I3 I4
查找M(I1),如果非零,则完成。
比较M(I1)== 0,如果为零,则重复I2的上一步骤
否则,在具有1个MSB位的第二个查找表中查找I2,如果非零,则表示您已完成。
否则,重复I3的上一步。
等等。实际上不要在I1-4上循环任何东西,而是完全展开它。
总结:2个具有256个条目的查找表,247/256个案例通过一次查找解决,大约8/256个具有两个查找等等。
编辑:表格,为清晰起见(输入,位表2 MSB,位表1 MSB)
I table2 table1
0 00000000 00000000
1 00000000 00000001
2 00000000 00000010
3 00000011 00000010
4 00000000 00000100
5 00000101 00000100
6 00000110 00000100
7 00000110 00000100
8 00000000 00001000
9 00001001 00001000
10 00001010 00001000
11 00001010 00001000
12 00001100 00001000
13 00001100 00001000
14 00001100 00001000
15 00001100 00001000
16 00000000 00010000
17 00010001 00010000
18 00010010 00010000
19 00010010 00010000
20 00010100 00010000
..
250 11000000 10000000
251 11000000 10000000
252 11000000 10000000
253 11000000 10000000
254 11000000 10000000
255 11000000 10000000
答案 3 :(得分:1)
这是另一次尝试(没有循环,没有查找,没有条件)。这一次有效:
var orig=0x109;
var x=orig;
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
x = orig & ~(x & ~(x >> 1));
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
var solution=orig & ~(x >> 1);
Console.WriteLine(solution.ToString("X")); //0x108
可能会被比我聪明的人缩短。
答案 4 :(得分:1)
按照我之前的回答,这是完整的实施。我认为它的速度和它一样快。 (抱歉展开整件事; - )
#include <stdio.h>
unsigned char bittable1[256];
unsigned char bittable2[256];
unsigned int lookup(unsigned int);
void gentable(void);
int main(int argc,char**argv)
{
unsigned int challenge = 0x42341223, result;
gentable();
if ( argc > 1 ) challenge = atoi(argv[1]);
result = lookup(challenge);
printf("%08x --> %08x\n",challenge,result);
}
unsigned int lookup(unsigned int i)
{
unsigned int ret;
ret = bittable2[i>>24]<<24; if ( ret ) return ret;
ret = bittable1[i>>24]<<24;
if ( !ret )
{
ret = bittable2[i>>16]<<16; if ( ret ) return ret;
ret = bittable1[i>>16]<<16;
if ( !ret )
{
ret = bittable2[i>>8]<<8; if ( ret ) return ret;
ret = bittable1[i>>8]<<8;
if ( !ret )
{
return bittable2[i] | bittable1[i];
} else {
return (ret | bittable1[i&0xff]);
}
} else {
if ( bittable1[(i>>8)&0xff] )
{
return (ret | (bittable1[(i>>8)&0xff]<<8));
} else {
return (ret | bittable1[i&0xff]);
}
}
} else {
if ( bittable1[(i>>16)&0xff] )
{
return (ret | (bittable1[(i>>16)&0xff]<<16));
} else if ( bittable1[(i>>8)&0xff] ) {
return (ret | (bittable1[(i>>8)&0xff]<<8));
} else {
return (ret | (bittable1[i&0xff]));
}
}
}
void gentable()
{
int i;
for ( i=0; i<256; i++ )
{
int bitset = 0;
int j;
for ( j=128; j; j>>=1 )
{
if ( i&j )
{
bitset++;
if ( bitset == 1 ) bittable1[i] = i&(~(j-1));
else if ( bitset == 2 ) bittable2[i] = i&(~(j-1));
}
}
//printf("%3d %02x %02x\n",i,bittable1[i],bittable2[i]);
}
}
答案 5 :(得分:0)
使用this的变体,我想出了以下内容:
var orig=56;
var x=orig;
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
Console.WriteLine(orig&~(x>>2));
在c#中,但应该很容易翻译。
我不太确定我已经回答了你的问题。这需要最高位并保留它和它旁边的位,例如。 101 =&gt; 100
答案 6 :(得分:0)
这是一些应该有效的python:
def bit_play(num):
bits_set = 0
upper_mask = 0
bit_index = 31
while bit_index >= 0:
upper_mask |= (1 << bit_index)
if num & (1 << bit_index) != 0:
bits_set += 1
if bits_set == 2:
num &= upper_mask
break
bit_index -= 1
return num
一次通过该号码。它构建了一个它所穿过的位的掩码,因此它可以在触及第二个最重要的位时屏蔽掉底部位。 一旦找到第二位,它就会清除低位。您应该能够创建高位掩码,而 &=
代替第二个while循环。也许我会破解并编辑帖子。
答案 7 :(得分:0)
我也使用基于表格的方法,但我相信单独一个表就足够了。以4位的情况为例。如果输入保证有2或3位,那么您的输出只能是6个值中的一个
0011
0101
0110
1001
1010
1100
将这些可能的值放在按大小排序的数组中。从最大值开始,找到等于或小于目标值的第一个值。这是你的答案。对于8位版本,您将获得更多可能的返回值,但仍然可以轻松地小于8 * 7的最大可能排列。
public static final int [] MASKS = {
0x03, //0011
0x05, //0101
0x06, //0110
0x09, //1001
0x0A, //1010
0x0C, //1100
};
for (int i = 0; i < 16; ++i) {
if (countBits(i) < 2) {
continue;
}
for (int j = MASKS.length - 1; j >= 0; --j) {
if (MASKS[j] <= i) {
System.out.println(Integer.toBinaryString(i) + " " + Integer.toBinaryString(MASKS[j]));
break;
}
}
}
答案 8 :(得分:0)
这是我在C#中的实现
uint OnlyMostSignificant(uint value, int count) {
uint newValue = 0;
int c = 0;
for(uint high = 0x80000000; high != 0 && c < count; high >>= 1) {
if ((value & high) != 0) {
newValue = newValue | high;
c++;
}
}
return newValue;
}
使用count,你可以使它成为最重要的(计数)位。
答案 9 :(得分:0)
我的解决方案:
使用"The best method for counting bits in a 32-bit integer",如果答案为3,则清除低位。仅当输入限制为2或3位时才有效。
unsigned int c; // c is the total bits set in v
unsigned int v = value;
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count
crc+=value&value-(c-2);