位运算的一些必须具备的基础知识

266 阅读2分钟
  • 数字在底层存储都是32位的,long是64位
  • 左移没有符号,右移才会有符号

反码和补码

System.out.println(Integer.MAX_VALUE);会输出2147483647,换成二进制表示就是01111111111111111111111111111111,也就是23112^{31}-1

    public static void main(String[] args) {
        System.out.println(Integer.MAX_VALUE);
        print(Integer.MAX_VALUE);
    }
​
    private static void print(int num) {
        for (int i = 31; i >= 0; i--) {
            System.out.print((num & (1 << i)) == 0 ? "0" : "1");
        }
    }

Q:为什么最高一位是0?

A:如果是无符号数字,例如在C++里无符号整数,可以表示0~23212^{32}-1的数。但是在Java中,是有符号数,可表示的范围为231-2^{31}23112^{31}-1,也就是说一半分给负数,一半分给正数。就造成了有符号整数没有无符号整数大。

Q:为什么要-1

A:最高位是0,表示这个数肯定是非负的。最高位是1,代表肯定是负数,值是由后面的位取反,再+1。当这个数位非负时,可表示的数就是23212^{32}-1的数,为啥要-1呢?不然咋表示00是算在非负范围内,因为0的二进制所有位全是0,因为有0,这个最终所有数的范围,就要-1,也就意味着最大值比最小值的绝对值少1

例如-1,二进制就是11111111111111111111111111111111,最高位的1代表为负数,其余为取反,则均为0,再+1,为1,值就为1,和符号位结合,最终带符号的数值就为-1

Q:负数怎么表达?

A:最高位为1,剩下随便用。

Q:负数为啥要取反加一?

A:所有算数符号底层都是用二进制进行计算,例如加减乘除或与反,底层使用一套逻辑。如果负数不用取反加一,则意味着,加和减需要两套逻辑去处理。如果用一套逻辑,性能将会提升,简单高效,所以负数的二进制定义,才会如此扭曲。只能讲到这了,不能再展开了,简单理解,人家乐意。

右移

  • >>:最高位用符号位补齐,符号位是啥就补啥。
  • >>>:不带符号右移,用0补齐

取相反数

例如5,取相反数为-5。

  • 直接加减号
int a = 5;
int b = -a;
System.out.println(b);
  • 取反加一
int a = 5;
int c = (~a + 1);
System.out.println(c);

间接证明了上面加减乘除使用一套逻辑。

Q:最小的那个数,取相反数是啥?众所周知,非负数的范围是比负数少1的,那也就意味着最小值的相反数,没有与之对应的正数来表示,那么会是啥呢?

A:其实还是跟算相反数的逻辑有关。最小值的二进制状态,最高位是1,其余全是0。那取反加一后的值,一直进位,回去了,还是它自己,所以最小值的相反数还是它自己

这不是bug,所有语言都这样。如果真的要用Integer的最小值取相反数,那么你应该用long类型。

最小值:100000000...
取反:01111111.....
加1100000000....