一:常见的位运算使用场景
1. 位运算
位运算是按照整教的二进制位进行移位、与、或、非、异或的运算,应用于整数类型( int),长整型( long),短整型( short ),字符型( char ),和字节型( byte )等数据类型(浮点型不能进行位运算)。由于,位运算由于直接采用二进制进行计算,所以往往可以得到计算性能的提升。
Java中的常见位运算有6种:
<< 左移
>> 右移
& 位与
| 位或
~ 位非
^ 位异或
1.1.移位运算 << 或 >>
在计算机中,整数总是以二进制的形式表示。
例如:int类型的整数7使用4字节表示的二进制。
00000000 0000000 0000000 00000111
对整数7进行左移,作用等同于n×2^位运算 ,结果如下:
int n = 7; // 00000000 00000000 00000000 00000111 = 7
int a = n << 1; // 00000000 00000000 00000000 00001110 = 14 相当于 7 乘 2的1次方
int b = n << 2; // 00000000 00000000 00000000 00011100 = 28 相当于 7 乘 2的2次方
int c = n << 28; // 01110000 00000000 00000000 00000000 = 1879048192 相当于 7 乘 2的28次方
int d = n << 29; // 11100000 00000000 00000000 00000000 = -536870912 相当于 7 乘 2的29次方
注意:
左移29位时,由于最高位变成1,因此结果变成了负数。
对整数28进行右移,作用等同于 n ÷ 2^位移数 ,结果如下:
int n = 28; // 00000000 00000000 00000000 00011100 = 28
int a = n >> 1; // 00000000 00000000 00000000 00001110 = 14 = 28 除 2的1次方
int b = n >> 2; // 00000000 00000000 00000000 00000111 = 7 = 28 除 2的2次方
int c = n >> 3; // 00000000 00000000 00000000 00000011 = 3 = 28 除 2的3次方
简单来理解:左移实际上执行乘法,右移实际上执行除法
1.2.与运算 &
&与运算的规则是:必须两个数同时为1,结果才为1
public class Main {
public static void main(String[] args) {
int i = 167776589; // 00001010 00000000 00010001 01001101
int n = 167776512; // 00001010 00000000 00010001 00000000
int x = i & n; // 00001010 00000000 00010001 00000000
System.out.println(x); // 167776512
}
}
1.3.或运算 |
|或运算的规则是:只要任意一个为1,结果就为1
public class Main {
public static void main(String[] args) {
int i = 167776589; // 00001010 00000000 00010001 01001101
int n = 167776512; // 00001010 00000000 00010001 00000000
int x = i | n; // 00001010 00000000 00010001 01001101
System.out.println(x); // 167776589
}
}
1.4.非运算 ~
~非运算的规则是:0和1互换(反转)
public class Hello {
public static void main(String[] args) {
int i = 167776589; // 00001010 00000000 00010001 01001101
int x = ~i; // 11110101 11111111 11101110 10110010
System.out.println(x); // -167776590
}
}
1.5.异或运算 ^
^异或运算的规则是:如果两个数不同,结果为1,否则为0。
public class Hello {
public static void main(String[] args) {
int i = 167776589; // 00001010 00000000 00010001 01001101
int n = 167776512; // 00001010 00000000 00010001 00000000
int x = i ^ n; // 00000000 00000000 00000000 01001101
System.out.println(x); // 77
}
}
2.常见用途
●移位运算,计算指定数值n的50%:n >> 1
●移位运算,计算指定数值n的2倍:n << 1
●与运算,判断奇偶数
○a&1 == 0 偶数
○a&1 == 1 奇数
●求平均值,防止溢出
○(x&y)+((x^y)>>1)
●异或运算,交换两个整数
二:整数类型运算时的类型溢出问题,产生原因以及解决办法
1.溢出
由于整数存在范围限制,如果计算结果超出了范围,就会产生溢出,而溢出不会出错,会得到一个奇怪的结果。
例如:
public class Main {
public static void main(String[] args) {
int x = 2147483640;
int y = 15;
int sum = x + y;
System.out.println(sum); // -2147483641
}
}
解释上述结果:把整数2147483640和15换成二进制做加法,观察一下运算过程。
0111 1111 1111 1111 1111 1111 1111 1000
+ 0000 0000 0000 0000 0000 0000 0000 1111
-----------------------------------------
1000 0000 0000 0000 0000 0000 0000 0111
由于最高位计算结果为1,因此,加法结果变成了一个负数。
2.解决方法
解决上面的问题,可以把int换成long类型,由于long可表示的整型范围更大,所以结果就不会溢出:
long x = 2147483640;
long y = 15;
long sum = x + y;
System.out.println(sum); // 2147483655
注意:如果超出long范围可以使用BigInteger
三:浮点类型运算时的精度丢失问题,产生原因以及解决办法
1.浮点数精度丢失
浮点数运算和整数运算相比,只能进行加减乘除这些数值计算,不能做位运算和移位运算。在计算机中,浮点数虽然表示的范围大,但是,浮点数有个非常重要的特点,就是浮点数常常无法精确表示,会出现精度丢失问题。
例如:
浮点数0.1在计算机中就无法精确表示,因为十进制的0.1换算成二进制是一个无限循环小数,很显然,无论使用float还是double,都只能存储一个0.1的近似值。但是,0.5这个浮点数又可以精确地表示。
由于,浮点数常常无法精确表示,因此,浮点数运算也会产生误差。
例如:
public class Main {
public static void main(String[] args) {
double x = 10 - 9.9;
double y = 1.0 - 9.0 / 10;
// 观察x和y是否相等:
System.out.println(x);
System.out.println(y);
// 判断x是否等于y
System.out.println(x == y);
System.out.println(Math.abs((x-y)) < 0.00001);
}
}
2.解决方法
在java中可以使用BigDecimal类
BigDecimal可以表示一个任意大小且精度完全准确的浮点数。
BigDecimal bd = new BigDecimal("123.4567");
System.out.println(bd.multiply(bd)); // 15241.55677489