前置知识
Java虚拟机中的整数
在java虚拟机中整数使用补码表示。整数类型有byte、short、int、long四种,分别表示 8位、16位、32位、64位。
原码
原码是指将最高位作为符号位(0表示正,1表示负),其它数字位代表数值本身的绝对值。以8位bit为例如:
反码
码表示规则为:如果是正数,则表示方法和原码一样;如果是负数,符号位不变,其余各位取反,则得到这个数字的反码表示形式。如:
补码
补码是计算机表示二进制数据的一般方式,其规则为:如果是整数,则表示方法和原码一样;如果是负数,如-6,则将-6的反码加上1,相当于将+6的原码数值位取反加1。
补码的优势:
- 能做到符号位和数值部分一起运算,这样无需单独考虑符号。
- 能把减法运算转化为加法运算来处理。
- 补码没有正0和负0之分,所以表示范围比原码和反码多1个。
例子
数字6 | +6 | -6 |
---|---|---|
原码 | 0000 0110 | 1000 0110 |
反码 | 0000 0110 | 1111 1001 |
补码 | 0000 0110 | 1111 1010 |
位运算实现加减乘除运算
一、加法
在十进制中,以 13 + 9 = 22
为例:
- 只考虑求和sum,不考虑进位:sum = 13 + 9 = 12;
- 只考虑进位carry:carry = 13 + 9 = 10;
- 如果carry != 0,重复步骤1,2;当carry=0时,返回最终的sum。
在二进制中,上面的三个计算步骤同样适用,并且有:
- 求和部分相当于异或运算:
异或: 1^1=0, 1^0=1, 0^0=0
- 计算进位相当于做与运算后再左移一位:
位与: 1&1=1, 1&0=0, 0&0=0
num左移i位:num << i
其中13的二进制为0000 1101,9的二进制为0000 1001,套用十进制中的3个步骤即有:
- 只求和:sum = 0000 1101 ^ 0000 1001 = 0000 0100;
- 只进位:carry = (0000 1101 & 0000 1101) << 1 = 0001 0010;
- carry != 0,则:
重复1:sum =sum + carry = 0000 0100 + 0001 0010 = 0001 0110 = 1 * 2 ^ 4 + 1 * 2 ^ 2 + 1 * 2 ^ 1 = 22
重复2:carry = 0
退出,返回sum=22。
接着就可以写出加法器的实现了:
/**
* 递归法
*/
private int addWithRecursive(int a, int b){
if (b == 0) return a; // 递归终止条件,如果右加数为0,即不再有进位了,则结束。
int sum = a ^ b;
int carry = (a & b) <<1 ; // 进位左移1位,达到进位的目的。
return addWithRecursive(sum, carry);
}
/**
* 非递归法
*/
private int add(int a, int b){
int sum = 0;
int carry = 0;
while(b != 0){
sum = a