用位运算实现加减乘除(1)
用位运算实现+-*/
1.加法
2.减法
3.乘法
4.除法
其实计算机处理加减乘除,都是使用位运算的。在计算机的底层全是位运算帮你实现的。计算机不认识什么加减乘除,计算机底层只认逻辑门,只认识与或左移右移之类的,底层都是01信号。但是手写的位运算还是不如java自带的快,因为java虚拟机里面有一定的汇编语言的。自己写的还需要经过翻译。底层的自带的位运算是很快的,但是你在java里使用位运算实现加减乘除,可能还不如java自带的原始的加减乘除快~。
a+b怎么算?
举例:


要知道,异或运算就是无进位相加。异或运算,也叫无进位相加。

这是a^b,无进位相加得到的结果:

a和b&后的结果,再向左移1位,就是进位信息:

原始的a+b 其实就是等于,a+b(无进位信息) + 进位信息。
比如a+b无进位相加得到0111010 = 58,进位信息为1000 = 8,相加得66。

把a+b无进位相加得到的结果记为a' , 进位信息记为b',继续上述过程:a'+b'无进位相加,+ 进位信息:

因为还有进位信息,继续上述过程:



到这一步进位信息没了,a''''+b''''就是答案了。
计算机里面加法的实现就是这套流程。
a-b怎么办?
a-b = a + b的相反数。
b的相反数怎么表示? b的相反数 = ~b + 1
于是就是这么调用:add(a, add(~b, 1))
a*b怎么办呢?
来到小学是怎么学乘法的:

二进制的乘法怎么做呢?

其实也是和十进制的乘法一样,如上图,最后将每行的结果相加,我自己草稿算了一下得出如下:

简化一点,想转化为代码,怎么写:
b的第1位是1,我就知道a进去
b的第2位是1,我在a后面补个0,再进去
b的第3位是1,我在a后面再补个0,再进去
b的第4位是0,不进去

代码解释如下,用下图所示的a和b的计算过程来代入代码:

第1次进入while循环,运行到38行,a <<= 1时,如下:

a左移1,b右移1之后:

第2次循环,b !=0,b & 1 = 0,跳过if,a左移1位,b右移1位:

第3次循环,b != 0, b & 1 = 1, a进去,res累加:

紧接着,a左移1位,b右移1位:

此时b=0,循环结束,返回ans。
为什么要写b>>>1,右移,左边最高位一律拿0补。
如果写b >> 1,表示最高位拿符号位补,如果b为负数的话,那就一直补1,代码就跑不完了。

代码实现
package com.cy.class05;
/**
* 位运算实现+-*\\/
* 测试连接:https://leetcode.com/problems/divide-two-integers
*/
public class Code03_BitAddMinusMultiDiv {
public static int add(int a, int b) {
int sum = a;
while(b != 0) {
sum = a ^ b; //无进位相加信息 -> sum
b = (a & b) << 1; //进位信息 -> b -> b'
a = sum; //无进位相加信息a -> a'
}
return sum;
}
/**
* 返回n的相反数
* 相反数 = 取反 + 1
* -n = ~n + 1
*/
public static int negNum(int n) {
return add(~n, 1);
}
public static int minus(int a, int b){
return add(a, negNum(b));
}
/**
* 乘法
*
* 右移:
* b >> 1,表示右移1,最高位拿符号位来补。
* b >>>1, 表示右移1,最高位一律拿0来补。
*
* 支持负数,道理不好解释,负数的情况下到底发生了什么,跟补码有关,你只要知道同样这套方法既支持正数也支持负数。
*/
public static int multi(int a, int b) {
int res = 0;
while(b != 0) {
if ((b & 1) != 0) {
res = add(res, a);
}
a <<= 1;
b >>>= 1;
}
return res;
}
public static void main(String[] args) {
int a = 7;
int b = -3;
System.out.println(multi(a, b));
}
}
--
浙公网安备 33010602011771号