• Java位运算及补码存储


    大家都知道,计算机中二进制数值是以补码的形式存储的,那么为什么二进制数值在原码、反码、补码中选择以补码的形式存储呢?

    一、原码形式存储

    首先,原码是站在用户角度的,是原始的二进制!

    求原码步骤:

    1. 用户的数字分为正负数,需要有一位存储符号
    2. 最高位为符号位:0为正,1为负
    3. 左边是高位,右边是低位

    由原码的计算方式可以发现源码存储会引发2个问题:

    (1)0有两个存储方式

    我们以char型(占1字节,8位)为例(下同):

    +0:    0000 0000

    - 0:    1000 0000

    不难发现+0和-0的原码是不一样的,而在计算过程中,+0和-0是没有任何区别的。

    (2)正数和负数相加,结果不正确(计算机只会加)

    1 - 1 =1 + (-1)

     1:  0000 0001

    -1:  1000 0001

              1000 0010  =  -2

    很显然1-1=0,而用原码进行计算得出的结果却相差甚远!

    二、反码形式存储

    既然原码不适合作为计算机的存储方式,人们在解决这个问题的过程中又提出了反码的概念

    求反码步骤:

    1. 求原码
    2. 符号为不变,其他位取反
    3. 注意:正数原码、反码一样 !

    接下来我们检验一下1 - 1:


      1:    0000 0001

    - 1:    1111 1110

                 1111 1111 -> 1000 000 ( 转换为原码,因为原码是站在用户角度的 )  =  -0

    不难可能出反码已经解决的正负数相加结果不正确的问题

    然后我们再检验+0、-0


    +0  0000 0000

    -0   1111 1111

    由此看出,反码并没有解决0有两种形式的问题


    三、补码形式存储

    在反码的基础上,人们有提出了补码的概念

    求补码:

    补码为其反码+1


    注意:正数的原码、反码、补码都一样!

    接下来我们对补码存储进行验证:

        +0: 0000 0000

         -0: 原码:  1000 0000

                  反码:  1111 1111

                  补码:10000 0000(char占1字节八位,最高位丢弃)= 0000 0000

     可以看出补码解决了+0 -0不一样的问题

        +1: 0000 0001

         -1: 1111 1111 

              1 0000 0000(最高位丢弃)= 0000 0000 = 0

     那么补码也解决了正负数相加结果不正确的问题! 

    四、总结:

    补码是在原码的基础上为了适应计算机运算一步一步完善而来,原码和反码存储都有一定的弊端,因此计算机采用补码存储!


    Java提供的位运算符有:

    左移( << )、右移( >> ) 、无符号右移( >>> ) 、位与( & ) 、位或( | )、位非( ~ )、位异或( ^ ),除了位非( ~ )是一元操作符外,其它的都是二元操作符。

    1、左移( << )

    将5左移2位:5<<2

    首先会将5转为2进制表示形式 ( java中,整数默认就是int类型,也就是32位):

    0000 0000 0000 0000 0000 0000 0000 0101           然后左移2位后,低位补0

    0000 0000 0000 0000 0000 0000 0001 0100           换算成10进制为20


    2、右移( >> ) 

    5>>2
    还是先将5转为2进制表示形式:
    0000 0000 0000 0000 0000 0000 0000 0101 然后右移2位,高位补0

    0000 0000 0000 0000 0000 0000 0000 0001

    3、无符号右移( >>> )

    我们知道Java中int类型占32位,既可以一个正数,也可以表示负数。其中最高位是符号位,正数为0,负数为1

    例如  -5换算成二进制后为(补码形式:原码取反 + 1):

    1111 1111 1111 1111 1111 1111 1111 1011   

    我们分别对5进行右移3位、 -5进行右移3位和无符号右移3位:


    5>>3      结果是0
    -5>>3     结果是-1
    -5>>>3   结果是536870911

    5换算成二进制: 0000 0000 0000 0000 0000 0000 0000 0101

    5右移3位后结果为0,0的二进制为: 0000 0000 0000 0000 0000 0000 0000 0000     (高位补0

     -5换算成二进制: 1111 1111 1111 1111 1111 1111 1111 1011

    -5右移3位后结果为-1,-1的二进制为: 1111 1111 1111 1111 1111 1111 1111 1111   (高位补1

    -5无符号右移3位后的结果 536870911 换算成二进制: 0001 1111 1111 1111 1111 1111 1111 1111   (高位补0

    通过其结果转换成二进制后,我们可以发现,正数右移,高位用0补,负数右移,高位用1补,当负数使用无符号右移时,用0进行补位(自然而然的,就由负数变成了正数了)

    注意:这里说的是右移,高位补位的情况。正数或者负数左移,低位都是用0补。

    4、位与( & )

    5 & 3

    5转换为二进制:0000 0000 0000 0000 0000 0000 0000 0101

    3转换为二进制:0000 0000 0000 0000 0000 0000 0000 0011

    -------------------------------------------------------------------------------------

    1转换为二进制:0000 0000 0000 0000 0000 0000 0000 0001

    位与:第一个操作数的的第n位于第二个操作数的第n位如果都是1,那么结果的第n为也为1,否则为0

    5、位或( | )

    5 | 3
    5转换为二进制:0000 0000 0000 0000 0000 0000 0000 0101

    3转换为二进制:0000 0000 0000 0000 0000 0000 0000 0011

    -------------------------------------------------------------------------------------

    7转换为二进制:0000 0000 0000 0000 0000 0000 0000 0111


    位或操作:第一个操作数的的第n位于第二个操作数的第n位 只要有一个是1,那么结果的第n为也为1,否则为0

    6、位异或( ^ )

    5 ^ 3
    5转换为二进制:0000 0000 0000 0000 0000 0000 0000 0101

    3转换为二进制:0000 0000 0000 0000 0000 0000 0000 0011

    -------------------------------------------------------------------------------------
    6转换为二进制:0000 0000 0000 0000 0000 0000 0000 0110


    位异或:第一个操作数的的第n位于第二个操作数的第n位 相反,那么结果的第n为也为1,否则为0


    7、位非( ~ )           

    ~5
     5转换为二进制:0000 0000 0000 0000 0000 0000 0000 0101
    -------------------------------------------------------------------------------------

    -6转换为二进制:1111 1111 1111 1111 1111 1111 1111 1010

    位非:操作数的第n位为1,那么结果的第n位为0,反之。

    由位运算操作符衍生而来的有:

    &= 按位与赋值

    |=  按位或赋值

    ^= 按位非赋值

    >>= 右移赋值

    >>>= 无符号右移赋值

    <<= 赋值左移

    和 += 一个概念而已。

    参考:

    https://blog.csdn.net/qq_41727218/article/details/79521759

    https://blog.csdn.net/xiaochunyong/article/details/7748713

  • 相关阅读:
    Http学习(一)
    Android Studio 创建aar包与引用
    C语言模块化编译介绍
    程序结构和分支语句介绍
    数据类型、常量、变量、printf、scanf和运算符
    第一个C语言程序
    Xcode相关整理
    Java: IO 学习小结
    Java: RandomAccessFile
    Java: IO 字符流
  • 原文地址:https://www.cnblogs.com/codestarer/p/13635542.html
Copyright © 2020-2023  润新知