1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > java 无符号运算_不用加减乘除符号实现四则运算(整数)--JAVA

java 无符号运算_不用加减乘除符号实现四则运算(整数)--JAVA

时间:2024-03-14 07:29:24

相关推荐

java 无符号运算_不用加减乘除符号实现四则运算(整数)--JAVA

这种面试题...能想到的就是用位运算代替

在讲解之前,首先普及一点知识

与运算(全一才是一):

0 & 0 = 0

1 & 0 = 0

0 & 1 = 0

1 & 1 = 1

或运算(有一就是一):

0 | 0 = 0

1 | 0 = 1

0 | 1 = 1

1 | 1 = 1

非运算(就是唱反调):

~1 = 0

~0 = 1

异或运算(不同才是一):

0 | 0 = 0

1 | 0 = 1

0 | 1 = 1

1 | 1 = 0

移位运算:

左移末尾自动补0

000010 << 1 = 000100

右移,含符号,首部补符号位

1110110 >> 1 = 11110111

右移,无符号,首部自动补零

1110110 >>> 1 = 01111011

好了,普及就到这了,接着往下看吧

-----------------------可爱的分割线---------------------------------

加法:

因为位运算都是针对二进制的,我们不太好理解,那我们看一下十进制的时候咱是怎么算的:

12+19 = 31

首先,从末尾开始相加,加完后溢出的部分放到前面与下一位相加,这是幼儿园的做法;

那我们换个思路,首先不考虑进位,首先逐位相加,得到的结果与进位结果相加,不断迭代直到没有进位;

那上面那个题

12+19,逐位相加,

得到21,进位10,逐位相加,

31,进位0,得到结果。

其实二进制也是一样的,加法问题就转化成了:

1.获取逐位相加的结果

2.获取进位的值

不断将这两个结果在此相加,直到没有进位,那不就是最终的结果了嘛(笑)

单个位的加法规则:

0 + 0 = 0 (进位 0)

1 + 0 = 1 (进位 0)

0 + 1 = 1 (进位 0)

1 + 1 = 0 (进位 1)

发现没,逐位加法和异或操作结果一样,获取进位和与操作后左移一位结果一样;

解决了,代码如下:

public int add(int a, int b){

while(b != 0){

int sum = a ^ b;

int pos = (a & b) << 1;

a = sum;

b = pos;

}

return a;

}

-----------------------可爱的分割线---------------------------------

减法:

emmmmmmmmmmmm,起初我还想了很久,后来真是被自己蠢到了

a - b 不就是 a + (-b) 么......

那问题就变成了怎么用位运算取到 -b

这个..就很简单了 -n = ~n+1

代码如下:

public int minus(int a,int b) {

return add(a, ~b+1) ;

}

对了,我们不是不让用加减乘除符号嘛,好好好,我改...

public int minus(int a,int b) {

return add(a, add(~b, 1));

}

-----------------------可爱的分割线---------------------------------

乘法:

符号问题的话,不考虑,全按整数算,大不了最后取反就是了。

最简单的,乘法不就是多个加法嘛,

对,引用我高中数学老师的话,我不能说你错,但你这也不对

因为太--------慢--------了--------

我们的算法最差也会在32次循环内结束,如果累加的话,就会导致计算规模随b绝对值的增大而增大,这不是我们希望看到的。

还是那个思路,正常十进制是怎么算的

12*56,首先个位相乘,十位相乘后乘十,百位乘十后乘百....最后结果相加

沿着这个思路,到了二进制反而简单了,为什么呢

首先,乘数只有0和1啊,即全清空和全保留,好像.. 啥也不用做诶

乘十,到了这里就变成了乘2,那....左移就可以了

至于结果累加.....我们上面不是做过了嘛~

看代码吧:

public int multi(int a, int b){

int ans = 0;

int index = 0;

while(b != 0){

if( (b&1) == 1){// 判断最后一位是不是1

ans = add(ans, a << index);

}

index = add(index, 1);

b = b >> 1;

}

return ans;

}

-----------------------可爱的分割线---------------------------------

emmmmmmmmmmmm,我早就想吐槽楼上了,一点都不可爱好吗!

呼,终于到最后一个了,还是最简单的思路,反复的减去除数直至被除数小于除数就行了

但又回到了问题规模的问题,如果被除数过大,除数过小,就会出现循环次数过多的问题

这次我们就用乘法的逆运算,先找结果中第一个一的位置,即满足被除数>=除数<

然后相减,即贪心算法

代码如下:

public int divide(int a, int b) {

int flag = 1;

// 处理符号问题

if(a >> 31 == 1){

a = add(~a, 1);

flg = add(~flg, 1);

}

if(b >> 31 == 1){

b = add(~b, 1);

flg = add(~flg, 1);

}

int re = 0;

while (a >= b){

int aa = 1, temp = b;

while (temp < (a>>1)){

temp <<= 1;

aa <<= 1;

}

a -= temp;

re += aa;

}

return re*flag;

}

-----------------------可爱的分割线---------------------------------

(我丢~楼上砍你哦!)

总结,

上面的操作有一些位运算的技巧在这里说一下:

b & 1 :取一个数的最后一位

~n+1 :对n取反

-n &n :整数二进制串中最后一个1

n&(n-1) :去掉整数二进制串中最后一个1

n ^ n = 0

其实,这篇文章还是不太专业的,比如,举例时用的都是整数,细心的同学可能发现我没有加判定条件,那负数为什么也适用呢?这就涉及到计算机编码的知识了,其实就是补码和反码,有兴趣的同学合一自行百度;

还有就是没有考虑到整数溢出的问题,int型变量的取值范围是[-2^31, 2^31-1],对于

(2^31-1)+1这样的操作是会造成溢出的,但如何处理不在我们今天的讨论范围,有兴趣的同学可以自行百度

好啦,就这么多吧,这里只是一个位运算的引子,其实计算机还是蛮有趣的,共勉。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。