1 找出一个整数的二进制表示位中1的个数
如:36 100100,其中有2个'1'。
首先来看一个最牛的算法,没有跳转,可以并行处理的:
#define POW(c) (1<<(c))
#define
MASK(c) (((unsigned long)-1) / (POW(POW(c)) + 1))
#define
ROUND(n, c) (((n) & MASK(c)) + ((n) >> POW(c) & MASK(c)))
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
int
bit_count(unsigned
int
n)
{
n = ROUND(n, 0);
n = ROUND(n, 1);
n = ROUND(n, 2);
n = ROUND(n, 3);
n = ROUND(n, 4);
return n;
}
如:36 100100,其中有2个'1'。
首先来看一个最牛的算法,没有跳转,可以并行处理的:
#define POW(c) (1<<(c))
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
{
n = ROUND(n, 0);
n = ROUND(n, 1);
n = ROUND(n, 2);
n = ROUND(n, 3);
n = ROUND(n, 4);
return n;
}
其实是分治算法的应用:
1 0 1 1 1 1 0 0 0 1 1 0 0 0 1 1 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1
|
0 1
|
1 0
|
1 0
|
0 0
|
0 1
|
0 1
|
0 0
|
1 0
|
0 1
|
1 0
|
1 0
|
0 1
|
1 0
|
1 0
|
1 0
|
1 0
|
0 0 1 1
|
0 0 1 0
|
0 0 1 0
|
0 0 1 0
|
0 0 1 1
|
0 0 1 1
|
0 1 0 0
|
0 1 0 0
|
0 0 0 0 0 1 0 1
|
0 0 0 0 0 1 0 0
|
0 0 0 0 0 1 1 0
|
0 0 0 0 1 0 0 0
|
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
|
0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0
|
1位数目计数的好方法是,首先设置每个2位字段为原来的两个位的和,然后,求相临2位字段的和,把结果放入相应的4位字段,以此类推.应此,对32位无符号整数,实际上要做的是
x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f);
x = (x & 0x00ff00ff) + ((x >> 8) & 0x00ff00ff);
x = (x & 0x0000ffff) + ((x >> 16) & 0x0000ffff);
上面的算法中:
上面的算法中:
MASK(0) = 0x55555555 = 01010101010101010101010101010101
b
MASK(1) = 0x33333333 = 00110011001100110011001100110011
b
MASK(2) = 0x0f0f0f0f = 00001111000011110000111100001111
b
MASK(3) = 0x00ff00ff = 00000000111111110000000011111111
b
MASK(4) = 0x0000ffff = 00000000000000001111111111111111 b
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
ROUND中对n的处理:(n & MASK) + (n >> POW & MASK)
-------------------
普通的算法
int bit_count(unsigned int n)
{
int count;
for(count = 0; n; n >>= 1)
{
count += n & 1;
}
return count;
}
int bit_count(unsigned int n)
{
int count;
for(count = 0; n; n >>= 1)
{
count += n & 1;
}
return count;
}
int bit_count(unsigned int n)
{
int count;
for(count = 0; n; n &= n - 1)
{
count++;
}
return count;
}
解析:
{
int count;
for(count = 0; n; n &= n - 1)
{
count++;
}
return count;
}
解析:
如何只数'1'的个数?如果一个数字至少包含一个'1'位,那么这个数字减1将从最低位开始依次向高位借位,直到遇到第一个不为'0'的位。依次借位使得经过的位由原来的'0'变为'1',而第一个遇到的那个'1'位则被借位变为'0'。
36 d = 100100 b
36-1 d = 100011 b
如果最低位本来就是'1',那么没有发生借位。
现在把这2个数字做按位与:n & n-1的结果:
36 d = 100100 b
36-1 d = 100011 b
如果最低位本来就是'1',那么没有发生借位。
现在把这2个数字做按位与:n & n-1的结果:
36 & 36-1 d = 100000 b去掉了后面的1。
现在再做一次100000 & 011111 = 0。所有的1都去掉。最后计数出来就是1的个数。
现在再做一次100000 & 011111 = 0。所有的1都去掉。最后计数出来就是1的个数。
n&(n-1)还有一个妙用,用于判断一个数是否是2的幂. 其特点就是 只有最高位为1,其余位都为0. 1特殊为2的0次幂.0要排除.
则n&(n-1) == 0 即为2的n次幂
则n&(n-1) == 0 即为2的n次幂
2 位运算的一些用法
用位运算实现两个数的交换
x ^=y;
y^=x;
x^=y;
y^=x;
x^=y;
原因:两次异或运算即为原值.
求绝对值:
int abs( int x )
{
int y ;
int abs( int x )
{
int y ;
y = x >> 31 ;
return (x^y)-y ; //or: (x+y)^y
}
3 位运算与大小端问题
用宏实现小端到大端格式的转换:
unsigned long int htonl(unsigned long int hostlong) {
#if __BYTE_ORDER==__LITTLE_ENDIAN
return (hostlong>>24) | ((hostlong&0xff0000)>>8) |
((hostlong&0xff00)<<8) | (hostlong<<24);
#else
return hostlong;
#endif
}
linux 中对16位, 32位高低调换的做法:
unsigned long int htonl(unsigned long int hostlong) {
#if __BYTE_ORDER==__LITTLE_ENDIAN
return (hostlong>>24) | ((hostlong&0xff0000)>>8) |
((hostlong&0xff00)<<8) | (hostlong<<24);
#else
return hostlong;
#endif
}
linux 中对16位, 32位高低调换的做法:
#define __bswap_constant_16(x) \
((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
#define __bswap_constant_32(x) \
((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
(((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。例如,16bit宽的数0x1234在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
#define __bswap_constant_32(x) \
((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
(((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。例如,16bit宽的数0x1234在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址
|
0x4000
|
0x4001
|
存放内容
|
0x34
|
0x12
|
而在
Big-endian
模式
CPU
内存中的存放方式则为:
内存地址
|
0x4000
|
0x4001
|
存放内容
|
0x12
|
0x34
|
32bit
宽的数
0x12345678
在
Little-endian
模式
CPU
内存中的存放方式(假设从地址
0x4000
开始存放)为:
内存地址
|
0x4000
|
0x4001
|
0x4002
|
0x4003
|
存放内容
|
0x78
|
0x56
|
0x34
|
0x12
|
而在
Big-endian
模式
CPU
内存中的存放方式则为:
内存地址
|
0x4000
|
0x4001
|
0x4002
|
0x4003
|
存放内容
|
0x12
|
0x34
|
0x56
|
0x78
|
判断大小端的方法:
1) 联合体 union 的存放顺序是所有成员都从低地址开始存放,面试者的解答利用该特性,轻松地获得了 CPU 对内存采用 Little-endian 还是 Big-endian 模式读写。
1) 联合体 union 的存放顺序是所有成员都从低地址开始存放,面试者的解答利用该特性,轻松地获得了 CPU 对内存采用 Little-endian 还是 Big-endian 模式读写。
int checkCPU( )
{
union w
union w
{
int a;
char b;
} c;
c.a = 1;
return(c.b ==1);
}
返回真:小端格式,否则大端格式.
short int x;
char x0,x1;
x=0x1122;
x0=((char*)&x)[0]; //低地址单元
x1=((char*)&x)[1]; //高地址单元
若x0=0x11,则是大端; 若x0=0x22,则是小端......
返回真:小端格式,否则大端格式.
short int x;
char x0,x1;
x=0x1122;
x0=((char*)&x)[0]; //低地址单元
x1=((char*)&x)[1]; //高地址单元
若x0=0x11,则是大端; 若x0=0x22,则是小端......
转载于:https://blog.51cto.com/lifj07/230592