2014-03-26 62 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

相關問題