位操作

位操作

如何实现位操作求两个数的平均值

一般而言,求解平均值的方法就是两数相加除以2,但是采用这种方法会存在一个问题,当两个数比较大时,如两者的和大于机器位数能够表示的最大值,可能会存在数据溢出的情况,而采用位运算的方法则可以避免这一问题。

代码示例:

#include <stdio.h>

int main()
{
	int x = 2147483647;
	int y = 2147483647;
	
	printf("%d\n",(x+y)/2);//存在数据溢出的情况 
	printf("%d\n",(x&y)+((x^y)>>1));
	
	return 0;
}


如何利用位运算计算数的绝对值

以x为负数为例来分析。因为在计算机中,数字都是以补码的形式存放的,求负数的绝对值,就应该不管符号位,执行按位取反,末位加1操作。

对于一个负数,将其右移31位后变成0xffffffff,而对于一个整数而言,右移31位则为0x00000000,而0xffffffff^x+x=-1,因为1011^1111=0100,任何数与1111异或,其实质都是把x的0和1进行颠倒计算。如果用变量y表示x右移31位,(x^y)-y则表示的是x的绝对值。

代码示例:

#include <stdio.h>

int MyAbs(int x)
{
	int y;
	y = x >> 31;
	return (x^y)-y; //此处还可以写为(x+y)^y 
}
int main()
{
	printf("%d\n",MyAbs(2));
	printf("%d\n",MyAbs(-2));
	
	return 0;
}


 

如何求解整型数的二进制表示中1的个数

求解整型数的二进制表示中1的个数有以下两种方法

代码示例:

#include <stdio.h>
//方法1 
int func1(int x)
{
	int countx = 0;
	
	while(x)
	{
		countx++;
		x = x & (x-1);
	}
	return countx;
}


//方法2 
int func2(unsigned int n)
{
	int count = 0;
	while(n)
	{
		count += n & 0x1u;  //0x1u表示十六进制的无符号数1 
		n >>= 1;
	}
	return count;
}

int main()
{
	printf("func1:%d\n",func1(9999));
	printf("func2:%d\n",func2(9999));
		
	return 0;
}


理解方法1中的操作:

1)当一个数被减1时,它最右边的那个值为1的bit将变成0,同时其右边的所有的bit都会变成1;

2)“&=”,位与并赋值操作。去掉已经被计数过的1,并将该值重新设置给n.这个算法循环的次数是bit位为1的个数。也就是说,有几个bit为1,循环几次,对bit为1比较稀疏的数来说,性能很好。

 

方法2判断每个数的二进制表示中每一位是否为1,如果为1,就在count上加1,而循环的次数是常数,即n的位数。该方法缺陷是在1比较稀疏的时候效率会比较低。

 

 

不能用sizeof()函数,如何判断操作系统是16位还是32位

代码示例:

#include <stdio.h>

//方法1 
int main()
{
	int i = 65536;
	printf("%d\n",i);
	int j = 65535;
	printf("%d\n",j);
	
	return 0;
} 
//16位机器输出为:0     -1
//32位机器输出为:65536   65535
#if 0
int main()
{
	unsigned int a = ~0;
	if(a > 65536)
	{
		printf("32位\n");
	}
	else
	{
		printf("16位\n");
	}
	return 0;
}
#endif
 


方法1:一般而言,机器位数不同,其表示的数字的最大值也不同,根据这一特性,可以判断操作系统位数。之所以有区别,是因为在16位操作系统下,能够表示的最大数为65535,所以会存在最高位溢出的情况。当变量的值为65535时,输出为0;当变量值为65535时,输出为-1.而在32位机器上,则不会出现溢出的情况,所以正常输出。

方法2:对0值取反,不同位数下的0值取反,其结果也不一样。例如,在32位机器下,按位取反运算,其结果为11111111111111111111111111111111.

 

如何判断计算机处理器是大端还是小端?

#include <stdio.h>

int main()
{
	union checkcpu
	{
		int a;
		char ch;
	};
	
	checkcpu CPU;
	CPU.a = 0x12345678;
	
	if(CPU.ch == 0x78)
	{
		printf("小端字节序\n");
	}
	else
	{
	    printf("大端字节序\n");	
	} 
	return 0;
} 


考虑n位二进制数,有多少个数中不存在相邻的1?

当n=1时,满足条件的二进制数为0、1,一共两个数;当n=2时,满足条件的二进制数有00、01、10,一共3个数;当n=3时,满足条件的二进制数有000,001,010,100,101,一共5个数。对于n位二进制,设所求结果为a(n),对于第n位的值,分为0或者1两种情况:

1)第n位为0,则有a(n-1)个数

2)第n位为1,则要满足没有相邻为1的条件,第n-1位为0,有a(n-2)个数,因此得到结论a(n)=a(n-1)+a(n-2)

通过观察发现,满足斐波拉契数列,因此代码示例:

//考虑n位二进制数,有多少个数中不存在两个相邻的1
#include <stdio.h>

long Fibonacci(int i)
{
    if(i == 1 || i ==2)
	{
	    return 1;	
	}	
	else
	{
		return (Fibonacci(i-1) + Fibonacci(i-2));
	}
}

int main()
{
	printf("%ld\n",Fibonacci(7));
	
	return 0;
} 




不用除法操作符如何实现两个正整数的除法?

//不用除法操作符如何实现两个正整数额除法
#include <stdio.h>

//方法1:可以根据除法运算的原理进行减法操作,对除数循环减被除数,减一次结果加1,直到刚好减为0,或者余数小于被除数为止。 
int MyDiv1(int a,int b)
{
	int result;
	
	if(b == 0)
	{
		printf("除数不能为0\n");
		
		return result;
	}
	while(a >= b)
	{
		result++;
		a = a-b;
	}
	return result;
} 

//方法2:递归法求解。采用比较数翻倍的比较方法,算法效率将得到极大优化
int MyDiv2(int a,int b)
{
	int k = 0;
	int c = b;
	int res = 0;
	
	if(b == 0)
	{
		printf("除数不能为0\n");
	}
	if(a < b)
	{
		return 0;
	}
	for(; a>=c; c <<= 1,k++)
	{
		if(a - c < b)
		{
			return 1 << k;
		}
	}
	return MyDiv2(a-(c>>1),b)+(1 << (k-1));
} 

//方法3:采用移位操作实现,位操作效率一般都比较高效
int MyDiv3(int x,int y)
{
    int left_num = x;
	int result = 0;
	while(left_num >= y)
	{
		int multi = 1;
		while(y * multi <= (left_num >> 1))
		{
			multi = multi << 1;
		}
		result += multi;
		left_num -= y * multi;
	}
	return result;
} 
int main()
{
	printf("%d\n",MyDiv1(10,3));
	printf("%d\n",MyDiv2(10,3));
	printf("%d\n",MyDiv3(10,3));
	
	return 0;
}


不用加法操作符如何实现两个正整数的加法?

#include<stdio.h>

int add1(int num1,int num2)
{
	if(0 == num2)
	{
		return num1;
	}
	int sumTemp = num1 ^ num2;
	int carry = (num1 & num2) << 1;
	
	return add1(sumTemp,carry);
}

int add2(int num1,int num2)
{
	int sum = 0;
	int num3 = 0;
	int num4 = 0;
	while((num1 & num2) > 0)
	{
		num3 = num1 ^ num2;
		num4 = num1 & num2;
		num1 = num3;
		num2 = num4 << 1;
	}
	sum = num1 ^ num2;
	
	return sum;
}
int main()
{
	printf("%d\n",add1(100,200));
	printf("%d\n",add2(100,200));
	
	return 0;
}


 

不用乘法操作符如何实现两个正整数的乘法?

#include <iostream>
#include <map>

using namespace std;

int mul(int a, int b)
{
	bool neg = (b < 0);
	if(b < 0)
	{
		b = -b;
	}
	int sum = 0;
	map<int,int>bit_map;
	for(int i = 0; i < 32; i++)
	{
		bit_map.insert(pair<int,int>(1 << i,i));
	}
	while(b > 0)
	{
		int last_bit = bit_map[b & ~(b-1)];
		sum += (a << last_bit);
		b &= b -1;
	}
	if(neg)
	{
		sum = -sum;
	}
	return sum;
}

int main()
{
	printf("%d\n",mul(3,5));
	
	return 0;
}


 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值