unsigned char reverse8( unsigned char c )
{
c = ( c & 0x55 ) << 1 | ( c & 0xAA ) >> 1;
c = ( c & 0x33 ) << 2 | ( c & 0xCC ) >> 2;
c = ( c & 0x0F ) << 4 | ( c & 0xF0 ) >> 4;
return c;
}算法
unsigned long func(unsigned long x)
{
x = (x & 0x55555555UL) + ((x >> 1) & 0x55555555UL);
x = (x & 0x33333333UL) + ((x >> 2) & 0x33333333UL);
x = (x & 0x0f0f0f0fUL) + ((x >> 4) & 0x0f0f0f0fUL);
x = (x & 0x00ff00ffUL) + ((x >> 8) & 0x00ff00ffUL);
x = (x & 0x0000ffffUL) + ((x >> 16) & 0x0000ffffUL);
return x;
}函數
x&=x-1;.net
本帖轉自http://blog.csdn.net/todototry/archive/2007/04/23/1575900.aspxblog
可是更加詳細的說明以下:get
這兩個函數極非常巧妙,做了並行計算。數學
先看問題1: 反轉一個字節。
它的算法是這樣的: 首先是2位2位爲一組,交換前一半和後一半。再4位4位爲一組,交換前一半和後一半。再8位爲一組,交換前一半和後一半it
。
可能還有點說不清楚。我舉個例子。
將1 2 3 4 5 6 7 8 反轉。
(1)2個2個爲一組,交換前一半和後一半, 變成。
2 1 4 3 6 5 8 7
(2)4個4個爲一組,交換前一半和後一半, 變成
4 3 2 1 8 7 6 5
(3)再8個爲一組,交換前一半和後一半, 變成
8 7 6 5 4 3 2 1
反轉成功。
這樣的算法原本非常簡單,很容易用數學概括法證實其正確。這函數, 巧妙就巧妙在做了並行計算,分組,它一次就計算完了。原理
先看第一個語句。c = ( c & 0x55) << 1 | ( c & 0xAA ) >> 1;
0x55其實就是01010101, 0xAA就是10101010
假設 c=abcdefgh
c & 0x55 = 0b0d0f0h, c & 0xAA = a0c0e0g0
跟着,前者左移一位, b0d0f0h0, 後者右移一位, 0a0c0e0g, 再一個|運算,就兩位兩位交換了位置。
想象一下,你有一個長紙條,分紅一格一格,每格寫一個字,假如你將紙條每隔一格剪一個小洞,滑一格,覆蓋在原來的紙條上,你就會看到兩個兩個字交換了位置。
(注: |運算能夠換成+運算,想想爲何)二進制
第二個語句。 c = ( c & 0x33 ) << 2 | ( c & 0xCC ) >> 2;
0x33 = 00110011, 0xCC=11001100。並行
第三個語句。c = ( c & 0x0F ) << 4 | ( c & 0xF0 ) >> 4;
0x0f = 00001111, 0xF0=11110000.
這兩個語句的做用也是 分組,將一半位變成0,移位滑動,跟着再組合,就分組交換了位置。
不防想象兩個小紙條剪洞疊加。
這方法應該能夠推廣。
理解了問題1,也就很容易理解問題2了.
問題2: 判斷32位整數二進制中1的個數。
和問題1同樣,也是採用了分組並行計算。
基本方法是: 2位2位爲一組,相加,看看有幾個1。再4位4位爲一組,相加,看看有幾個1......
仍是說的不太明白。接着分析。
爲了簡單說明,先看看8位的情形。相應地,函數裏面的語句變成。
x = (x & 0x55) + ((x >> 1) & 0x55); (1)
x = (x & 0x33) + ((x >> 2) & 0x33); (2)
x = (x & 0x0f) + ((x >> 4) & 0x0f); (3)
return x;
假設x=abcdefgh. 0x55=01010101
x & 0x55 = 0b0d0f0h. (x>>1) & 0x55 = 0a0c0e0g。相加。就能夠知道2位2位一組1的個數。
好比x=11111111
x= (x & 0x55) + ((x >> 1) & 0x55); 以後x=10101010。你2位2位地看,10=2, 就是2 2 2 2, 就是說各組都是2個1。
好比x=00101001
x= (x & 0x55) + ((x >> 1) & 0x55); 以後x=00010101。你2位2位地看,就是0 1 1 1, 前1組只有0個1,後面的組都是1個1。
好啦。再來看。0x33=00110011。
x=abcdefgh.
x=(x & 0x33)+((x >> 2)&0x33); 至關於, 00ab00ef + 00cd00gh。
由於語句(1)以後。ab指示了頭兩位有多少個1,cd指示了下兩位有多少個1。相加00ab+00cd就指示前4位有多少個1。這樣就是4位4位爲一組。注意這樣的分組,組與組之間永遠都不會產生進位的。正由於不會產生進位,才能夠分開來看。
好啦。下面的過程都是同樣的,再也不多說。
8位,16位,32位都同樣。
反過來推導,基本思想仍是2分法。
給你一個x,設f(x,n)表示x中2進制位1的個數,則
f(x)=f(x & 0x0000ffffUL) + f((x >> 16) & 0x0000ffffUL)
一個32位的化成兩個16位的,16位的再化成兩個8位的,並且能夠就在x上進行計算,保持數量不變,一直1位的x自己32位。
再從前面日後走
unsigned long func(unsigned long x)
{
第一步,把x當作32個數,每一個數就是一個bit,它自己在數量上就表示它們1的個數,
5的二進制是0101,就是01010101,運算完成後,它將成爲16個數,相鄰兩個位的數聯合表示原來1的個數,好比:
00 01 10 11 >>1= 00 00 11 01
& 01 01 01 01 & 01 01 01 01
= 00 01 00 01 = 00 00 01 01
+
= 00 01 01 10
=( 0 1 1 2)
完成以後,原數據將成爲相鄰2位組成一個數,共16個數(當作)。
x = (x & 0x55555555UL) + ((x >> 1) & 0x55555555UL);
第二步,把這16個數,又每相鄰兩個相加,這時候一個數是2位,因而相與的數就是 00110011,即0x33333333UL,原理同上,運算結束後,結果又縮小爲8個數,每一個數用4位表示,表示是對應4位的1的個數,好比:
( 0 1 1 2)
0001 0110 >>2= 0000 0101
& 0011 0011 & 0011 0011
= 0001 0010 0000 0001
+
= 0001 0011
=( 1 3)
後面同理。 x = (x & 0x33333333UL) + ((x >> 2) & 0x33333333UL); x = (x & 0x0f0f0f0fUL) + ((x >> 4) & 0x0f0f0f0fUL); x = (x & 0x00ff00ffUL) + ((x >> 8) & 0x00ff00ffUL); x = (x & 0x0000ffffUL) + ((x >> 16) & 0x0000ffffUL); return x; }