一、常见位运算的使用
位运算是按照整数的二进制位进行移位、与、或、非、异或的运算,应用于整数类型(int)、长整型(long)、短整型(short)、字符型(char)和字节型(byte)等数据类型(浮点型不能进行位运算)。由于,位运算直接采用二进制进行计算,所以往往可以得到计算性能的提升。
1.1 移位运算 << 或 >>
在计算机中,整数总是以二进制的形式表示
例如:int类型的整数7使用4字节表示的二进制
00000000 00000000 00000000 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 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);
}
}
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);
}
}
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
}
}
1.6 常见用途
- 移位运算,计算指定数值n的50%:n >> 1
- 移位运算,计算指定数值n的2倍:n << 1
- 与运算,判断奇偶数
- a&1 == 0 偶数
- a&1 == 1 奇数
- 求平均值,防止溢出
- (x&y)+((x^y)>>1)
- 异或运算,交换两个整数
int x = 3 , y = 7;
x = x ^ y;
y = x ^ y;
x = x ^ y;
二、溢出
由于整数存在范围限制,如果计算结果超出了范围,就会产生溢出,而溢出不会出错,会得到一个奇怪的结果。
例如:
public class Main{
public static void main(String[] args){
int x = 2147483640;
int y = 15;
int sum = x + y;
System.out.println(sum)//-2147283641
}
}
解释上述结果:把整数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,因此,加法结果变成了一个负数
解决上面的问题,可以把int换成long类型,由于long可表示的整型范围更大,所以结果不会溢出:
long x = 2147483640;
long y = 15;
long sum = x + y;
System.out.println(sum);//2147483655
三、浮点数精度丢失
浮点数运算和整数运算相比,只能进行加减乘除这些数值计算,不能做位运算和移位运算。在计算机中,浮点数虽然表示的范围大,但是,浮点数有个非常重要的特点,就是浮点数常常无法精确表示,会出现精度丢失问题。
例如:
浮点数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);
}
}
要解决浮点数精度丢失问题,可以考虑以下几种方法:
-
使用BigDecimal类:BigDecimal类提供了高精度的十进制运算,可以避免浮点数精度丢失的问题。使用BigDecimal类进行计算时,需要使用其提供的方法进行加减乘除等操作。
-
设置合适的舍入模式:在使用浮点数进行计算时,可以通过设置合适的舍入模式来控制精度。Java中的Math类提供了一些舍入模式,如ROUND_HALF_UP、ROUND_HALF_DOWN等,可以根据具体需求选择合适的舍入模式。
-
尽量避免直接比较浮点数:由于浮点数的精度问题,直接比较两个浮点数是否相等可能会出现误差。可以使用范围比较或者比较它们的差值是否小于一个很小的阈值来判断它们是否近似相等。
以上是我个人对Java中数值类型的运算方式的一些总结,如有错漏请务必联系,非常感谢。