用位运算实现四则运算之加减乘除
2015-06-18 09:05
543 查看
转自:http://www.cnblogs.com/dandingyy/archive/2012/10/29/2745570.html
以及:http://blog.csdn.net/luckyxiaoqiang/article/details/6886489
我认为原文中有错误的地方,被我改动了
^: 按位异或;&:按位与; | :按位或;~按位取反
计算机系统中,数值一律用补码来表示:因为补码可以使符号位和数值位统一处理,同时可以使减法按照加法来处理。
对补码做简单介绍:数值编码分为原码,反码,补码,符号位均为0正1负。
正数补码:与原码相同。 例如,+9的原码和补码都是00001001
下面两句感觉原文中作者总结错了,给我给更正了下:
负数原码 -> 补码: 数值位取反加1。
负数补码 -> 原码: 对该补码的数值位逐位 取反加1。
已知一个数的补码,求原码的操作分两种情况: (1)如果补码的符号位为“0”,表示是一个正数,所以补码就是该数的原码。 (2)如果补码的符号位为“1”,表示是一个负数,求原码的操作可以是:符号位为1,其余各位取反,然后再整个数加1。 例如,已知一个补码为11111001,则原码是10000111(-7):因为符号位为“1”,表示是一个负数,所以该位不变,仍为 “1”;其余7位1111001取反后为0000110;再加1,所以是10000111。
补码 的绝对值(称为真值):正数的真值就是本身,负数的真值是各位(包括符号位)取反加1(即变成原码并把符号位取反)。
b -> -b : 各位(包括符号位)取反加1。
int a=7;a在32位的计算机中表示如下:00000000 00000000 00000000 00000111
则~a在32位的计算机中表示如下: 11111111 11111111 11111111 11111000;
为补码,转换为原码则为 10000000 00000000 00000000 00001000 也就是-8
加法运算:将一个整数用二进制表示,其加法运算就是:相异(^)时,本位为1,进位为0;同为1时本位为0,进位为1;同为0时,本位进位均为0.
所以,不计进位的和为sum = a^b,进位就是arr = a&b,(与sum相加时先左移一位,因为这是进位)。完成加法直到进位为0.
减法运算:a-b = a+(-b) 根据补码的特性,各位取反加1即可(注意得到的是相反数,不是该数的补码,因为符号位改变了)
(上面用二进制实现的加减法可以直接应用于负数)
乘法运算:原理上还是通过加法计算。将b个a相加,注意下面实际的代码。
除法运算:除法运算是乘法的逆。看a最多能减去多少个b,
加法运算代码如下:
求相反数:
判断数字是否为正数,负数,或0:
用位操作求两个数的平均值:
(x&y)+((x^y)>>1)如数x:01010,y:01100。x&y为01000,即取x,y中对应位都为1的位,并将结果的对应位置1,相当于取得了都为1的位相加的一半。 x^y结果为00110,即取x,y中对应位只有一个为1的位,并将结果的对应位置1,由于最终结果是平均值,故除以2(用右移1位实现)。以上两部分结果相加可得两数的平均值。
乘法运算:
乘法就是将其中的一个乘数写成(2^0)*k0 + (2^1)*k1 + (2 ^2)*k2 + ... + (2^31)*k31,其中ki为0或1,然后利用位运算和加法就可以了。
除法运算:
原理:除法就是由乘法的过程逆推,依次减掉(如果x够减的)y^(2^31),y^(2^30),...y^8,y^4,y^2,y^1。减掉相应数量的y就在结果加上相应的数量。
以及:http://blog.csdn.net/luckyxiaoqiang/article/details/6886489
我认为原文中有错误的地方,被我改动了
^: 按位异或;&:按位与; | :按位或;~按位取反
计算机系统中,数值一律用补码来表示:因为补码可以使符号位和数值位统一处理,同时可以使减法按照加法来处理。
对补码做简单介绍:数值编码分为原码,反码,补码,符号位均为0正1负。
正数补码:与原码相同。 例如,+9的原码和补码都是00001001
下面两句感觉原文中作者总结错了,给我给更正了下:
负数原码 -> 补码: 数值位取反加1。
负数补码 -> 原码: 对该补码的数值位逐位 取反加1。
已知一个数的补码,求原码的操作分两种情况: (1)如果补码的符号位为“0”,表示是一个正数,所以补码就是该数的原码。 (2)如果补码的符号位为“1”,表示是一个负数,求原码的操作可以是:符号位为1,其余各位取反,然后再整个数加1。 例如,已知一个补码为11111001,则原码是10000111(-7):因为符号位为“1”,表示是一个负数,所以该位不变,仍为 “1”;其余7位1111001取反后为0000110;再加1,所以是10000111。
补码 的绝对值(称为真值):正数的真值就是本身,负数的真值是各位(包括符号位)取反加1(即变成原码并把符号位取反)。
b -> -b : 各位(包括符号位)取反加1。
int a=7;a在32位的计算机中表示如下:00000000 00000000 00000000 00000111
则~a在32位的计算机中表示如下: 11111111 11111111 11111111 11111000;
为补码,转换为原码则为 10000000 00000000 00000000 00001000 也就是-8
加法运算:将一个整数用二进制表示,其加法运算就是:相异(^)时,本位为1,进位为0;同为1时本位为0,进位为1;同为0时,本位进位均为0.
所以,不计进位的和为sum = a^b,进位就是arr = a&b,(与sum相加时先左移一位,因为这是进位)。完成加法直到进位为0.
减法运算:a-b = a+(-b) 根据补码的特性,各位取反加1即可(注意得到的是相反数,不是该数的补码,因为符号位改变了)
(上面用二进制实现的加减法可以直接应用于负数)
乘法运算:原理上还是通过加法计算。将b个a相加,注意下面实际的代码。
除法运算:除法运算是乘法的逆。看a最多能减去多少个b,
加法运算代码如下:
//递归版本的加法实现 int Add(int a, int b) { return b ? Add(a^b, (a&b)<<1) : a; } //该为迭代版本 int Add_iter(int a, int b) { int ans; while(b)//直到没有进位 { ans = a^b;//不带进位加法 b = (a&b)<<1;//进位 a = ans; } return ans; }
求相反数:
//求a的相反数:将各位取反加一 int negative(int a) //get -a { return Add(~a, 1); //~a表示a的数值位取反 } //也可以这样求a的相反数 int negative(int b) { int i; for( i = 1; i && ((b & i) ==0 ); i <<= 1)//先找到b的二进制位数中从右边数第一个不为0的位, ; for( i=i<< 1;i ; i =i<<1 ) //从下一位开始,从右向左,凡是b中为1的均置位0,为0的置为1; b ^= i; return b; } //如int b=6,则b的二进制表示为:00000000 00000000 00000000 00000110 则~b的二进制表示为: 11111111 11111111 11111111 11111001 通过上面的第二个negative(int b)求值后,b的二进制表示为:11111111 11111111 11111111 11111010减法运算:
//减法运算 int Minus(int a, int b) { return Add(a, negative(b)); }
判断数字是否为正数,负数,或0:
//判断是否是负数 int isneg(int a) { return a & 0x8000;//注意此处是0x80000000,网上有些地方是0x8000,结果会得出错误的结果 } //判断是否是0 int iszero(int a) { return !(a & 0xFFFF);//注意此处是0xFFFFFFFF,网上有些地方是0xFFFF,结果会得出错误的结果 } //判断是否是正数 int ispos(int a) { return (a&0xFFFF) && !(a&0x8000);//非0且非负 }
用位操作求两个数的平均值:
(x&y)+((x^y)>>1)如数x:01010,y:01100。x&y为01000,即取x,y中对应位都为1的位,并将结果的对应位置1,相当于取得了都为1的位相加的一半。 x^y结果为00110,即取x,y中对应位只有一个为1的位,并将结果的对应位置1,由于最终结果是平均值,故除以2(用右移1位实现)。以上两部分结果相加可得两数的平均值。
乘法运算:
乘法就是将其中的一个乘数写成(2^0)*k0 + (2^1)*k1 + (2 ^2)*k2 + ... + (2^31)*k31,其中ki为0或1,然后利用位运算和加法就可以了。
int Mul(int a,int b) //写的不是很完整,有待做溢出检测 { int ans = 0; for(int i = 1; i; i <<= 1, a <<= 1) if(b & i) ans += a; return ans; }
除法运算:
原理:除法就是由乘法的过程逆推,依次减掉(如果x够减的)y^(2^31),y^(2^30),...y^8,y^4,y^2,y^1。减掉相应数量的y就在结果加上相应的数量。
int divide(int dividend, int divisor) { if(isZero(divisor))//若分母为0,则返回正数最大值2147483647 return (numeric_limits<int>::max)(); if(isZero(dividend))//若分子为0,则返回0值 return 0; if(dividend==(numeric_limits<int>::min)()&&divisor==-1)//防止溢出,当最小负数值-2147483648除以-1时,返回正数最大值2147483647 return (numeric_limits<int>::max)(); int flag=-1;//结果符号位 if((isNeg(dividend)&&isNeg(divisor))||(!isNeg(dividend)&&!isNeg(divisor)))//符号为正 flag=1; unsigned int x=(unsigned int)abs(dividend);//求绝对值 unsigned int y=(unsigned int)abs(divisor); unsigned int re=0; for(int k=31;k>=0;--k) { if((x>>k)>=y)<span class="comment">/比较x是否大于y的(1<<i)次方,避免将x与(y<<i)比较,因为不确定y的(1<<i)次方是否溢出</span> { re+=1<<k; x-=y<<k; } } if(re>(numeric_limits<int>::max)()&&flag==-1)//若结果为负数,且大于整数表示范围,这种情况出现在-2147483648除以1上 return (numeric_limits<int>::min)(); else return flag*re; }
相关文章推荐
- 使用位运算实现网页中的过滤、筛选功能实例
- C#枚举中的位运算权限分配浅谈
- shell 基本计算、逻辑运算、位运算详解
- Java位运算和逻辑运算的区别实例
- 优秀程序员必须知道的20个位运算技巧
- JavaScript使用位运算符判断奇数和偶数的方法
- java位运算加密示例
- c语言中用位运算实现加法技巧介绍
- Java 位运算(移位、位与、或、异或、非)
- 位运算应用口诀和实例
- 【位运算】之 异或
- JavaScript 位运算笔记
- 想知道&&与&及||与|之间的区别吗?
- 想知道&&与&及||与|之间的区别吗?
- 不用if判断将字母进行大小写转换
- 位级运算的一点随笔
- 优秀程序员不得不知道的20个位运算技巧
- mysql位运算的应用
- 非10进制在Java中的应用
- 利用位运算实现加法运算