整数的二进制、位运算、逻辑与或

二进制 

  正整数的二进制表示 (假定类型是byte)

   正整数的二进制表示与此类似, 只是在十进制中,每个位置可以有10个数字,从0到9,但在二进制中,每个位置只能是0或1。

    例如: 0000 1010     ==>   10  

     负整数的二进制表示 (假定类型是byte)

  十进制的负数表示就是在前面加一个负数符号 -,例如-123。但二进制如何表示负数呢? 

  其实概念是类似的,二进制使用最高位表示符号位,用1表示负数,用0表示正数。

  但负数表示不是简单的将最高位变为1,比如说:

    •   byte a = -1,如果只是将最高位变为1,二进制应该是10000001,但实际上,它应该是11111111。

    •   byte a=-127,如果只是将最高位变为1,二进制应该是11111111,但实际上,它却应该是10000001。

 

  和我们的直觉正好相反,这是什么表示法?这种表示法称为补码表示法,而符合我们直觉的表示称为原码表示法,补码表示就是在原码表示的基础上取反然后加1。取反就是将0变为1,1变为0。

  负数的二进制表示就是对应的正数的补码表示,比如说:

    •   -1:1的原码表示是00000001,取反是11111110,然后再加1,就是11111111。

    •   -2:2的原码表示是00000010,取反是11111101,然后再加1,就是11111110

    •   -127:127的原码表示是01111111,取反是10000000,然后再加1,就是10000001。

       给定一个负数二进制表示,要想知道它的十进制值,可以采用相同的补码运算。比如:10010010,首先取反,变为01101101,然后加1,结果为01101110,它的十进制值为110,所以原值就是-110。

       byte类型,正数最大表示是01111111,即127,负数最大表示是10000000,即-128,表示范围就是 -128到127。其他类型的整数也类似,负数能多表示一个数。

 

负整数为什么采用补码呢?

  负整数为什么要采用这种奇怪的表示形式呢?原因是:只有这种形式,计算机才能实现正确的加减法。

  计算机其实只能做加法,1-1其实是1+(-1)。如果用原码表示,计算结果是不对的。比如说:

      

1   -> 00000001

-1 -> 10000001

+ ------------------

-2 -> 10000010

 

用符合直觉的原码表示,1-1的结果是-2。

 

如果是补码表示:

 

1   -> 00000001

-1 -> 11111111

+ ------------------

0  ->  00000000

 

结果是正确的。

 

再比如,5-3:

 

5   -> 00000101

-3 -> 11111101

+ ------------------

2  ->  00000010

 

结果也是正确的。

 

就是这样的,看上去可能比较奇怪和难以理解,但这种表示其实是非常严谨和正确的。

 

理解了二进制加减法,我们就能理解为什么正数的运算结果可能出现负数了。当计算结果超出表示范围的时候,最高位往往是1,然后就会被看做负数。比如说,127+1:

 

127   -> 01111111

1       -> 00000001

+ ------------------

-128  ->10000000

 

计算结果超出了byte的表示范围,会被看做-128。

补码的好处

  以 +1 和 -1 作加法运算为例,如下图所示:

  

  相信你已经发现,1 + (-1) 这样的加法运算只要将二进制数相加,然后-1的末位就会变成2,根据逢2进1机制,从右至左依次所有位都会变成0。

  最后,最左端的符号位也会进位1变成0,丢弃溢出的1,就得到最后的结果0的二进制表示32个0。

  对照本节开头的图,会发现所有的减法都可以转换成二进制位的加法运算:1-2 可以转换成1+(-2),(-1)-(-2)可以转换成-1+2……

  这跟数学中的表示是一样的,而且非常地方便计算(很多计算机科学家都是从数学领域转入计算机工程,所以在很多细微之处的都能见到数学的影子)。因此,现代计算机硬件结构实际上只设计了加法器,大部分的减法其实都是转换成加法后再运算。

   备注:

    以正数的二进制数表示为基准,负数的表示只改变符号位,这样的表示方式就是原码。因此,正数的表示方式都是原码。

    反码就是将原码除符号位以外的值全部取反,原来是1的变为0,原来是0的变为1。

    补码就是在反码的基础上,在二进制数的右端末位加1(逢2进1)。

小结

    正数的原码和反码和补码都一致;负数的原码是正数的符号位取反;负数的反码是原码的非符号位取反;负数的补码是反码加1。

 

位运算

  • 左移:操作符为<<,向左移动,右边的低位补0,高位的就舍弃掉了,将二进制看做整数,左移1位就相当于乘以2。

  • 无符号右移:操作符为>>>,向右移动,右边的舍弃掉,左边补0。

  • 有符号右移:操作符为>>,向右移动,右边的舍弃掉,左边补什么取决于原来最高位是什么,原来是1就补1,原来是0就补0,将二进制看做整数,右移1位相当于除以2。

逻辑与或

  两种逻辑与(&&和&)的运算规则基本相同,两种逻辑或(|| 和 |)的运算规则也基本相同。 &和|运算是把逻辑表达式全部计算完,而&&和||运算具有短路计算功能。

  所谓短路计算,是指系统从左至右进行逻辑表达式的计算,一旦出现计算结果已经确定的情况,则计算过程即被终止。 对于&&运算来说,只要运算符左端的值为false,则因无论运算符右端的值为true或为false,其最终结果都为false。 所以,系统一旦判断出&&运算符左端的值为false,则系统将终止其后的计算过程; 对于 || 运算来说,只要运算符左端的值为true,则因无论运算符右端的值为true或为false,其最终结果都为true。 所以,系统一旦判断出|| 运算符左端的值为true,则系统将终止其后的计算过程。

小技巧

  ⑴ 乘法除法:n * 2 等价于 n << 1; n * 5 等价于 n << 2 + 1; n / 2 等价于 n >> 1。

  备注:JVM执行时会自动转化,大部分其它高级语言的编译器会做类似优化转换,所以除非有特殊的理由,否则别这么写。

  ⑵ 取低位:n & 0x0000FFFF;取高位:n & 0xFFFF0000。

  ⑶ 奇偶判断:n & 1,等于0为偶,等于1为奇。

  ⑷ 正负判断:(n >>> 31) & 1,等于0为正,等于1为负。

  ⑸ 取余:n % m ,如m为2的幂次方,可用(n & (m - 1))替代。

 

 

参考:老马说编程

  

Java位运算符及二进制常识

posted @ 2018-02-23 12:47  TigerExpensive  阅读(3466)  评论(0编辑  收藏  举报