2014-03-26 35 views
3

在一个范围内的所有位让我们int为例:C:最有效的方法来设置内的变量

int SetBitWithinRange(const unsigned from, const unsigned to) 
{ 
    //To be implemented 
} 

SetBitWithinRange应该返回一个int,其中所有,只有位起始位from到位to被设置,当从smallerto并且都在范围032

例如为: int i = SetBitWithinRange(2,4)将导致i具有0B00值... 01100

+0

检查这[post](http://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit-in-cc)并适应它在一个循环中 – Coconop

+2

为什么你不尝试实现它,看看你能想出什么。 –

+1

这似乎可能是家庭作业..无论如何,这是一个想法。设置'n'位,然后通过'from'移动它们。 – harold

回答

5

这里有一些方法。首先,一些变体“设置n位,然后移动from”。我会用C#来回答,但我比C更熟悉它。应该很容易转换。

uint nbits = 0xFFFFFFFFu >> -(to - from); 
return nbits << from; 

缺点:不能处理一个空的范围,即,在情况下to <= from

uint nbits = ~(0xFFFFFFFFu << (to - from)); 
return nbits << from; 

上行:可以处理其中to = from在这种情况下,将设置无位的情况下。
下行:无法处理全部范围,即设置所有位。

这应该是显而易见的。

或者,您可以使用“减两两个权”招,

(1u << to) - (1u << from) 

缺点:to不能32,所以你永远无法设定最高位。

是这样工作的:

01000000 
    ^^^^^^ "to" zeroes 
    100 
     ^^ "from zeroes" 
-------- - 
00111100 

到1的右侧的“从”的一部分,它只是从零零被减去。然后在“从”部分1,你要么从减去1(如果to == from),并得到0的结果,否则你会从0在to减去1,并借一路1部分,这将被重置。

已经提出在写作的时候所有真正的逐位的方法有那些缺点之一,这提出了一个问题:能不能没有缺点呢?

答案是,很不幸,令人失望。它可以在没有缺点来完成,但只能通过

  1. 作弊(即使用非按位元素),或
  2. 超过操作将是很好的,或
  3. 不规范操作

举的1个例子,你可以随便挑任何以前的方法,并添加一个特殊的情况下(与if或三元运算符),以解决他们的缺点。

为了给出的2个例子:(未测试)

uint uppermask = (((uint)to >> 5)^1) << to; 
return uppermask - (1u << from); 

uppermask要么取1和移位它由to左(照常),或者它需要一个0,并转移它留下(由如果to == 32,这个数量无关紧要,因为它正在被移位)。但它有点奇怪,并使用更多的操作。

为了给出为3的示例中,给予零当由操作数大小或多个换档的换档将解决这个非常容易。不幸的是,这种转变并不常见。

1

我会用类似的东西去:

int answer = 0; 
unsigned i = from; 
for (; i <= to; ++i) 
    answer |= (1 << i); 

return answer; 

容易实现&可读。

我认为最快的方法是预先计算所有可能的值(从(0,0)到(32,32),如果你知道你将只用于32位整数)。其实大约有1000个。

然后你就会用O(1)解决方案,结束了:

answer = precalcTable[from][to]; 
3

OK,我拿起那@JohnZwinck已经朝我扔了战书。

如何: return (to<32 ? (1<<to) : 0) - (1<<from);

当然,这是不完全检查的fromto有效性。

根据@JosephQuinsey的评论进行编辑。

+2

或许将'1'改为'1U',因为'1 << 31'在32位机器上未定义行为。 –

+0

@JosephQuinsey,谢谢。 – Subway

+1

而你的问题说“范围从0到32”。但是'1U << 32'是典型的32位机器上IIRC未定义的行为。 –

1

可能:((1 << to) - (1 << from)) | (1 << to)

这也将设置往返位的要求

+0

对不起,我的问题是误导,我的意思是“解除”。编辑了这个问题。 – Subway

+0

@Subway你的意思是你想要0而不是1s? – fritzone

1

一种常见的方式来做到这一点有点高效地将是这样:

uint32_t set_bits_32 (uint32_t data, uint8_t offset, uint8_t n) 
{ 
    uint32_t mask = 0xFFFFFFFF >> (32-n); 

    return data | (mask << offset); 
} 
1

这里是我的答案。 (更新

unsigned int SetBits(int from, int to) 
{  
    return (UINT_MAX >> (CHAR_BIT*sizeof(int)-to)) & (UINT_MAX << (from-1)); 
} 

SetBits(9,16); ==> 0b 1111 1111 0000 0000 

SetBits(1,1); ==> 0b 0000 0001 // Just Bit #1 
SetBits(5,5); ==> 0b 0001 0000 // Just Bit #5 
SetBits(1,4); ==> 0b 0000 1111 // Bits #1, #2, #3, and #4 (low 4 bits) 
SetBits(1,32); ==> 0b 1111 1111 1111 1111 // All Bits 

然而,SetBits(0,0);并不会转弯的所有位下班了。

我的假设:

  • 位是基于1,从右侧开始。
  • 字节是8比特。
  • Ints可以是任何大小(16,32或64位)。使用sizeof(int)。
  • 未对fromto进行检查;来电者必须通过适当的值。
+1

+1为“Ints可以是任何大小”。顺便说一句:如果代码使用“CHAR_BIT”而不是8,那么可以放弃“字节为8位”的假设。 – chux

+0

谢谢!我不知道'CHAR_BIT'。正在更新... – abelenky

+0

注意:是否需要'UINT_MAX'而不是'INT_MAX'? – chux

0

也可以用这种方式完成,pow可以通过shift操作实现。

{ 
    unsigned int i =0; 
    i = pow(2, (to-from))-1; 
    i = i <<from; 
    return i; 
} 
+0

C标准定义的pow函数使用浮点,所以它是完全无效的。我认为你建议自己创建基于整数的pow()函数?不过,我不明白这将如何提高效率。 – Lundin

相关问题