7. Groovy 运算符-位运算符学习

2022-12-07 18:25:07 浏览数 (1)

1. 介绍

本篇是Groovy学习第7篇内容。上一篇学习了算术运算,关系运算和逻辑运算。今天接着上一篇,继续学习Groovy中的运算符相关知识。

今天主要介绍位运算符的相关知识。

位运算是直接针对数据的二进制数进行的一元或者二元操作。它的整体运算速度是要比我们普通的算术运算符的效率高。

但问题在于,它针对的是二进制数,与我们生活中使用的十进制计算方式不同。

操作的是0和1,我们无法直接通过表达式直观的心算出结果。

2. 按位比较运算符

下面,先介绍四种常见的按位计算符:

  • &: 按位与计算,"and"
  • |: 按位或计算, "or"
  • ^: 按位异或计算, "xor"
  • ~: 按位取反计算。 "not"

PS:Java中的位运算和Groovy是一样的。

位运算符可以应用于byteshortintlongBigInteger类型的参数。如果其中一个参数是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"
  • >>>: 无符号右移

当左边参数的类型为byteshortintlong时,这三个运算符都适用。前两个运算符也可以应用于左边参数为BigInteger类型的情况。如果左边参数是BigInteger,则结果将是BigInteger类型;如果左边参数为long,则结果将为long类型;否则,结果将为int类型。

而位移的标准写法都是 参数 >> 数量 或者:参数 << 数量 参数是我们要进行位移的值,而数量是我们要进行位移的距离,必须是整数。示例如下:

代码语言:javascript复制
    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 的二进制值取值流程:

  1. 先取5的原码:00000000 00000000 00000000 00000101。
  2. 获取反码: 11111111 11111111 11111111 11111010。
  3. 再将-号补上:11111111 11111111 11111111 11111011。
代码语言:javascript复制
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 了解。

本篇内容也是学习于该文档。并自己进行了位运算的相关细节补充。希望对位运算了解不深的小伙伴可以加深理解。

0 人点赞