1. 介绍
本篇是Groovy学习第7篇内容。上一篇学习了算术运算,关系运算和逻辑运算。今天接着上一篇,继续学习Groovy中的运算符相关知识。
今天主要介绍位运算符的相关知识。
位运算是直接针对数据的二进制数进行的一元或者二元操作。它的整体运算速度是要比我们普通的算术运算符的效率高。
但问题在于,它针对的是二进制数,与我们生活中使用的十进制计算方式不同。
操作的是0和1,我们无法直接通过表达式直观的心算出结果。
2. 按位比较运算符
下面,先介绍四种常见的按位计算符:
&
: 按位与计算,"and"|
: 按位或计算, "or"^
: 按位异或计算, "xor"~
: 按位取反计算。 "not"
PS:Java中的位运算和Groovy是一样的。
位运算符可以应用于byte
、short
、int
、long
或BigInteger
类型的参数。如果其中一个参数是BigInteger
,则结果将是BigInteger
类型;否则,如果其中一个参数是long
,则结果将是long
类型;否则,结果将为int
类型。
注意:位运算不管是什么语言中,它都不能用于浮点数计算,例如float和double 不能使用位运算符。
简单的示例代码如下:
代码语言:javascript复制class Zinyan {
def static main(def args) {
int a = 0b00101010 //我们创建一个二进制数 表示数字42,Groovy二进制数用0b开头
println(a) //打印输出: 42
int b = 0b00001000 //创建一个二进制数,表示数字8
println(b) //打印输出: 8
println(a & a) //按位与计算 得到:42
println(a & b) //按位与计算 得到: 8
println(a | a) //按位或计算 得到: 42
println(a | b) //按位或计算 得到: 42
int mask = 0b11111111
println((a ^ a) & mask) //按位异或 再执行按位与计算 得到:0
println((a ^ b) & mask) //按位异或 再执行按位与计算 得到:34
println((~a) & mask) //按位取反,再执行按位与计算 得到:213
}
}
下面我来详细拆解它的计算逻辑。
例如创建两个变量
代码语言:javascript复制int a = 0b00101010 //表示十进制数42
int b = 0b00001000 //表示十进制数8
//当我们执行按位与&计算时,会将两个数的二进制每一位进行比较,都是1留下。不同就设置0
a & b == 0b00001000 //也就会得到十进制数8
//当我们按位或计算时,将两个数的二进制中的每一位进行比较,只要有1就留下,没有1就舍弃。
a | b == 0b00101010
//按位异或计算,就是和按位或计算刚好相反的情况进行计算了。两个不同保留1,两个相同保留0
a ^ b == 0b00100010 //也就会得到十进制数34
//按位取反计算,通常都是针对数值本身进行的
~ a == 0b11010101 //十进制数-43
这种计算方式,不用必须输入二进制才能进行计算哦。上面的示例只是为了方便我们理解,才进行的。通常情况为
代码语言:javascript复制int a =42
int b =8
//按位计算
def c = a & b //c的值就是8
对于计算机来说,直接进行二进制操作运算速度当然就会比普通的加减乘除要快很多。因为中间的计算和转换步骤省略了。
总结:位运算符,就是将数值转为二进制数之后,两个二进制数 一点一点的比较0和1,是否相同,是否异同等等。 由于二进制数不方便直接阅读,所以这个位运算很容易计算混乱。
3. 移位运算符
上面是按位计算,这里来聊聊按位移动的运算符,Groovy中支持以下三种移动计算方式:
<<
: 左移 ,"Lsh">>
: 右移 ,"Rsh">>>
: 无符号右移
当左边参数的类型为byte
、short
、int
或long
时,这三个运算符都适用。前两个运算符也可以应用于左边参数为BigInteger
类型的情况。如果左边参数是BigInteger
,则结果将是BigInteger
类型;如果左边参数为long
,则结果将为long
类型;否则,结果将为int
类型。
而位移的标准写法都是 参数 >> 数量
或者:参数 << 数量
参数是我们要进行位移的值,而数量是我们要进行位移的距离,必须是整数。示例如下:
def static main(def args) {
println(3<<2) //将数值3进行左移两位。 得到的结果为12
println(3L << 3) //将Long数值3 左移三位,得到的结果为24
println(3 << 4) //将BigInteger类型数值3 左移4位 得到结果为48
println( -200 >>> 20) //将-200 无符号右移20位 得到正数 4095
println(-200 >> 20) //将-200 带符号右移20位, 得到-1
println(5 >> 1) //将数值5 右移1位, 得到2
println(-5 >> 1) //将数值-5 带符号右移1位 得到数值-3
}
下面将十进制数转为二进制数,来详细介绍它的移动逻辑。
代码语言:javascript复制def a = 0b00000011 //十进制数3 的二进制表示
//当我们进行左移2位时的效果如下:
a<<2 == 0b00001100 //该数值 十进制表示 12。
def b =0b00000101 //十进制数5的二进制表示结果。
//当我们进行右移1位的时候:
b>>1 == 0b00000010 // 最右边的那一位移出去了。所以结果值为2
上面都是针对整数的移动操作。而如果有负数会怎么样?想把握这个关键点就需要明白-号在二进制数值中的表现形式了。
在二进制码中,采用最高位是符号位的方法来区分正负数,正数的符号位为0、负数的符号位为1。剩下的就是这个数的绝对值部分。通过将负数转为二进制原码,再求其原码的反码,最后求得的补码即负数的二进制表示结果。
比如上面的-5 的二进制值取值流程:
- 先取5的原码:00000000 00000000 00000000 00000101。
- 获取反码: 11111111 11111111 11111111 11111010。
- 再将-号补上:11111111 11111111 11111111 11111011。
def a = 0b11111011 //表示的就是十进制-5了。
//当我们执行右移的时候,最右边的移出去之后,最左边补充的就会是1(因为带了符号所以为1)
a >> 1==0b11111101 // 结果值就是 -3了。 如果一直右移最后得到的值只能是-1了。
因为在二进制数中表示-1的值就是:11111111 11111111 11111111 11111111
。
而groovy中的>>>
无符号右移,意思就是移动之后左边补充的值是0,可以从负数移动成正数。
4. 小结
在编程之中,会有很多地方用到位运算,例如用一个整数表达多种状态的结果集。
如果我们想使用位运算提高我们的计算速度,但是又受限于二进制数的转换。可以使用windows电脑中自带的计算器的:程序员模式,进行二进制的各种计算,它可以将10进制数直接转为二进制数。
也可以进行各种位运算。不会操作的小伙伴可以参考我的这篇文章:https://zinyan.com/?p=287
Groovy官网有关位运算符的相关知识可以通过:http://docs.groovy-lang.org/docs/groovy-4.0.6/html/documentation/#_bitwise_and_bit_shift_operators 了解。
本篇内容也是学习于该文档。并自己进行了位运算的相关细节补充。希望对位运算了解不深的小伙伴可以加深理解。