java位运算符、移位运算符与加减乘除运算符之间的关系

一、位运算符主要针对二进制。包括‘与 & ’、‘或 | ’、‘非 ~’、‘异或  ^ ’。

1 .  与 运算符

与运算符用符号“&”表示,其使用规律如下:
两个操作数中,位都为1,结果才为1,否则结果为0。与运算符是二元运算符。

需要了解的知识点:

基本数据类型

java语言中,对应基本数据类型

在内存中所占内存空间大小

(B:byte,字节;b:bit,位;1B=8bit)

取值范围
byte、boolean1字节/1B/8bbyte: - 2^7~2^7-1 ;-128-127 ;存储数据量255
char、short2字节/2B/16bshort: - 2^15~2^15-1;-32768~32767;存储数据量 65535
int、float4字节/4B/32bint: - 2^31~2^31-1
long、double8字节/8B/64blong: - 2^63~2^63-1

实例:

属性二进制
a1290000 0000 0000 0000 0000 0000 1000 0001
b1280000 0000 0000 0000 0000 0000 1000 0000
a&b1280000 0000 0000 0000 0000 0000 1000 0000

代码:

public class Test {
    public static void main(String[] args){
        int a=129;
        int b=128;
        System.out.println("a 和b 与的结果是:"+(a&b));
    }
}

执行结果:

a 和b 与的结果是:128

Process finished with exit code 0

2. 或运算符
或运算符用符号“|”表示,其运算规律如下:
两个位只要有一个为1,那么结果就是1,否则就为0。 或运算符是二元运算符。

属性二进制
a1290000 0000 0000 0000 0000 0000 1000 0001
b1280000 0000 0000 0000 0000 0000 1000 0000
a|b1290000 0000 0000 0000 0000 0000 1000 0001

代码:

public class Test {
    public static void main(String[] args){
        int a=129;
        int b=128;
        System.out.println("a 和b 或的结果是:"+(a|b));
    }
}

执行结果:

a 和b 或的结果是:129

Process finished with exit code 0

3. 非运算符

非运算符用符号“~”表示,其运算规律如下:

如果位为0,结果是1,如果位为1,结果是0。非运算符是一元运算符。

实例:(涉及到补码,为-3。最高位是符号位,0-正值,1-负值)

属性二进制
a20000 0000 0000 0000 0000 0000 0000 0010
~a-31111  1111   1111 1111  1111  1111  1111 1101

知识点:源码、反码、补码

电脑的的世界中只有0和1,那么负数怎么表示呢?

        二进制的正负是从高位看,最高位如果1则是负数,如果是0则是正数。

        如果负数单纯是把最高位变为1的话,在运算中会出现不是我们想要的值,所以引入了:原码,反码,补码。

        正数的原码,反码,补码都一样,负数的反码是对除了符号位(最高位)对原码取反,补码是对反码+1

        负数的二进制转化,计算机计算是用的补码

1、首先取出这个数的原码的二进制,

2、然后再求出反码

3、最后求出补码
  例子:  -5

        -5的原码是                1000 0000 0000 0101

        求出反码的是            1111  1111  1111 1010

        求出补码是                1111  1111  1111 1011

本例中:~a二进制表示为 1111  1111 1111  1111 1111  1111  1111 1101,如何得出~a为-3呢

 二进制
补码1111  1111  1111  1111 1111  1111  1111 1101
反码1111  1111  1111  1111 1111  1111  1111 1100
源码1000 0000 0000 0000 0000 0000 0000 0011

源码为: 1000 0000 0000 0000 0000 0000 0000 0011

最高位(红色)是 1 ,表示为负值;数据位(绿色)是 000 0000 0000 0000 0000 0000 0000 0011;十进制表示为3;

所以源码表示的十进制值是 -3 。

代码:

public class Test {
    public static void main(String[] args){
        int a=2;
        System.out.println("a 非的结果是:"+(~a));
        System.out.println("~a的二进制表示为:"+Integer.toBinaryString(~a));
    }
}

执行结果:

a 非的结果是:-3
~a的二进制表示为:11111111111111111111111111111101

Process finished with exit code 0

4.异或运算符
异或运算符是用符号“^”表示的,其运算规律是:
两个操作数的位中,相同则结果为0,不同则结果为1。异或运算符是二元运算符。

属性二进制
a150000 0000 0000 0000 0000 0000 0000 1111
b20000 0000 0000 0000 0000 0000 0000 0010
a|b130000 0000 0000 0000 0000 0000 0000 1101

代码:

public class Test {
    public static void main(String[] args){
        int a=15;
        int b=2;
        System.out.println("a 和b 异或的结果是:"+(a^b));
    }
}

执行结果:

a 和b 异或的结果是:13

Process finished with exit code 0

二、移位运算符

java中有三种移位运算符

<<      :     左移运算符,num << 1,相当于num乘以2

>>      :     右移运算符,num >> 1,相当于num除以2

>>>    :     无符号右移,忽略符号位,空位都以0补齐

实例:(正数在输出二进制表示时,左面的0不输出)

属性二进制
a50000 0000 0000 0000 0000 0000 0000 0101
a<<2200000 0000 0000 0000 0000 0000 0001 0100
a>>210000 0000 0000 0000 0000 0000 0000 0001
a>>>210000 0000 0000 0000 0000 0000 0000 0001
b- 51111  1111  1111  1111  1111 1111  1111 1011
b<<2- 201111  1111  1111  1111  1111  1111 1110 1100
b>>1-31111  1111  1111  1111  1111  1111 1111 1101
b>>2- 21111  1111  1111  1111  1111  1111 1111 1110
b>>>210737418220011  1111  1111  1111  1111  1111  1111 1110

代码:

public class Test {
    public static void main(String[] args) {
        int a = 5;
        System.out.println("a是:" + a + "  二进制表示是:" + Integer.toBinaryString(a));
        System.out.println("a<<2的结果是:" + (a << 2) + "  二进制表示是:" + Integer.toBinaryString(a << 2));
        System.out.println("a>>2的结果是:" + (a >> 2) + "  二进制表示是:" + Integer.toBinaryString(a >> 2));
        System.out.println("a>>>2的结果是:" + (a >>> 2) + "  二进制表示是:" + Integer.toBinaryString(a >>> 2));
        int b = -5;
        System.out.println("b是:" + b + "  二进制表示是:" + Integer.toBinaryString(b));
        System.out.println("b<<2的结果是:" + (b << 2) + "  二进制表示是: " + Integer.toBinaryString(b << 2));
        System.out.println("b>>1结果是:" + (b >> 1) + "  二进制表示是: " + Integer.toBinaryString(b >> 1));
        System.out.println("b>>2的结果是:" + (b >> 2) + "  二进制表示是: " + Integer.toBinaryString(b >> 2));
        System.out.println("b>>>2的结果是:" + (b >>> 2) + "  二进制表示是: " + Integer.toBinaryString(b >>> 2));
    }
}

运行结果:

a是:5  二进制表示是:101
a<<2的结果是:20  二进制表示是:10100
a>>2的结果是:1  二进制表示是:1
a>>>2的结果是:1  二进制表示是:1
b是:-5  二进制表示是:11111111111111111111111111111011
b<<2的结果是:-20  二进制表示是: 11111111111111111111111111101100
b>>1结果是:-3  二进制表示是: 11111111111111111111111111111101
b>>2的结果是:-2  二进制表示是: 11111111111111111111111111111110
b>>>2的结果是:1073741822  二进制表示是: 111111111111111111111111111110

Process finished with exit code 0

三、位运算符与加减运算符之间的关系及实际运用。

关系:位运算符比加减运算符运算速度更快。一般能用位运算的就用位运算符。

实际运用:

实例1:输入一个整数,输出该数二进制表示中1的个数,如:9的二进制表示是1001,有2位是1,因此如果输入9,该函数输出2.

输入二进制表示输出
90000 0000  0000 0000 0000 0000 0000 10012

第一种思路:使用 >> 运算符,将数据右移,不断与数字 1做与 & 运算,若与运算结果为1,则该位置二进制表示为1,否则为0;

代码实现:

public class Test {
    public static void main(String[] args)
    {
        int a=9;
        System.out.println("数字 "+a+" 在内存中得二进制表示为:"+Integer.toBinaryString(a)+
                "  二进制表示法包含的位数是:"+ getNumOf1(a));
    }

    public static int getNumOf1(int n)
    {
        int number=0;
        while(n!=0)
        {
            if ((n&1)==1)
                number++;
            n=n>>1;
        }
        return number;
    }
}

程序执行结果:

数字 9 在内存中得二进制表示为:1001  二进制表示法包含的位数是:2

Process finished with exit code 0

问题:
       存在问题,移位运算中,当n时正数时,右移的话,最左面补0
        正值>> 左面补0     负值>> 左面补1   正负<<  右面补0

在本段代码中,当a是负数时,右移的话,最左面补位补的是1,一直右移就一直补1.会造成死循环 (1000)>>(1100)>>(1110)>>(1111)>>...>>1111。思考:有没有什么方法,让正负数 在 >> 操作时,左面均补位0 ?  答案: >>> 操作。

另外,如果我们首先判断数据是否是负值,负值的话,将数据变为相反数,即正值再判断1的个数,这种方法是不可取的,因为在内存中数据存储的是补码。不是源码。我们判断的是数据的补码中 1 出现的次数。将数据变为相反数后,数据在内存中表示,不止符号位发生变化,数据位也发生变化。

第二种思路:使用 >>> 运算符,将数据右移,不断与数字 1做与 & 运算,若与运算结果为1,则该位置二进制表示为1,否则为0;

代码:

public class Test {
    public static void main(String[] args)
    {
        int a=9;
        int b=-9;
        System.out.println("数字 "+a+" 在内存中得二进制表示为:"+Integer.toBinaryString(a)+
                "  二进制表示法包含的位数是:"+ getNumOf1(a));
        System.out.println("数字 "+b+" 在内存中得二进制表示为:"+Integer.toBinaryString(b)+
                "  二进制表示法包含的位数是:"+ getNumOf1(b));
    }

    public static int getNumOf1(int n)
    {
        int number=0;
        while(n!=0)
        {
            if ((n&1)==1)
                number++;
            n=n>>>1;
        }
        return number;
    }
}

程序执行结果:

数字 9 在内存中得二进制表示为:1001  二进制表示法包含的位数是:2
数字 -9 在内存中得二进制表示为:11111111111111111111111111110111  二进制表示法包含的位数是:31

Process finished with exit code 0

第三种思路:使用  << 运算符,flag=1,将flag左移,不断与数字 n做与 & 运算,若与运算结果不为0,则该位置二进制表示为1,否则为0直至flag为0;

这里红色字体:与运算结果不为0。与运算结果情况有两种

1. 与数据位 & ,结果大于0;

2. 与符号位 & ,若数据是负数,即符号位为1,则与结果- 2147483648(写个实例看看为何是这个数字),即: 是负数 ,若数据是正数,即符号位为0,则与结果为0。

代码:

 

public class Test {
    public static void main(String[] args)
    {
        int a=9;
        int b=-9;
        System.out.println("数字 "+a+" 在内存中得二进制表示为:"+Integer.toBinaryString(a)+
                "  二进制表示法包含的位数是:"+ getNumOf1(a));
        System.out.println("数字 "+b+" 在内存中得二进制表示为:"+Integer.toBinaryString(b)+
                "  二进制表示法包含的位数是:"+ getNumOf1(b));
    }

    public static int getNumOf1(int n)
    {
        int flag=1;
        int number=0;
        while(flag!=0)
        {
            if((flag&n) != 0)
                number++;
            flag=flag<<1;
        }
        return number;
    }
}

程序执行结果:

数字 9 在内存中得二进制表示为:1001  二进制表示法包含的位数是:2
数字 -9 在内存中得二进制表示为:11111111111111111111111111110111  二进制表示法包含的位数是:31

Process finished with exit code 0

第四种思路:有没有什么方法,只在1出现时计算,0时不计算?

对于任意数据n,n-1操作是将n的二进制表示中,最右面的1变为0,且最右面1的后面部分全取反。n=n & (n-1),则是将n的二进制表示中,最右面的1的后面全变为0。利用这一特性,统计二进制表示中1的个数。

nn-1

n=n & (n-1)

二进制表示中1出现的次数
11 - 0001 001110 - 0001 0010 10 - 0001 00101
10 - 0001 00109 - 0001 00018 - 0001 00002
8 - 0001 00007- 0000 1111 0 - 0000 00003
0 - 0000 0000 结束3

代码:

public class Test {
    public static void main(String[] args)
    {
        int a=9;
        int b=-9;
        System.out.println("数字 "+a+" 在内存中得二进制表示为:"+Integer.toBinaryString(a)+
                "  二进制表示法包含的位数是:"+ getNumOf1(a));
        System.out.println("数字 "+b+" 在内存中得二进制表示为:"+Integer.toBinaryString(b)+
                "  二进制表示法包含的位数是:"+ getNumOf1(b));
    }

    public static int getNumOf1(int n)
    {
        int number=0;
        while(n!=0)
        {
            number++;
            n=n&(n-1);
        }
        return number;
    }
}

程序执行结果:

数字 9 在内存中得二进制表示为:1001  二进制表示法包含的位数是:2
数字 -9 在内存中得二进制表示为:11111111111111111111111111110111  二进制表示法包含的位数是:31

Process finished with exit code 0

其他应用:

    /**用一条语句判断一个整数是不是2的整数次方 */
    public static boolean is2(int number)
    {
        if((number&(number-1))==0)
            return true;
        return false;
    }

    /**判断两个整数m和n,计算需要改变m的二进制表示中的多少位才能得到n */
    public static int Getn(int m,int n)
    {
        int dif=m^n;//一共有多少不同的位
        //求dif中的1的位置
        int number=0;
        while(dif!=0)
        {
            number++;
            dif=dif&(dif-1);
        }
        return number;
    }

    /** 题目:不使用加减乘除,完成两个数字的加法操作*/
    public static int add(int num1, int num2) {
        int sum = 0;
        while (num2 != 0) {
            sum = (num1 ^ num2);
            num2 = (num1 & num2) << 1;
            num1 = sum;
        }
        return num1;
    }
    
    /** n除以2 n/2 ,只取整数部分  (n是正数情况下成立) */
    n>>1;

    /** n乘以2 n/2 (正负数均成立)*/
    n<<1;


 

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值