概述
首先需要知道的是,在二进制的世界里,计算机用第一位来表示符号 0 是正数 1 是负数。
计算机里面存储的都是 补码,然后我们来看相关的知识
机器数
机器数就是计算机里面真实的高低电平.
- 1 在计算机中 存储 0000 0001 (补码)
- -1 在计算机中存储 1111 1111 (补码)
真值
真值就是机器数他真正表示的数字 0000 0001 (补码)这个机器数的真值就是1
原码反码补码
原码反码补码设计出来的根本原因是 为了让符号位参与计算机运算我们来看看逻辑推理
原码
原码就是符号位加上 真值 的绝对值。
1 的原码 是 0000 0001
-1 的原码是 1000 0001
反码
正数的反码是自身,负数的反码是符号位不变,其他的全部取反
1 的反码是 0000 0001
-1 的反码是 1111 1110
补码
正数的补码不变,负数的补码就是负数的反码加1
1 的补码是 0000 0001
-1 的补码是 1111 1111
补码的运算
1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补
计算机中 1000 0000 的补码就是表示 -128,可以比原码多表示一位数 所以在8位可以表示的数 [-128, 127]
位运算
java提供的位运算符有 : 左移 <<; 右移 >>; 无符号右移 >>> ,位与 & 位或 | 位非 ~, 位异或 ^
左移 (符号位也参与移动)
二进制数向左移动k位,丢弃最高的k位,并在有右边补k个0
-
01111111 11111111 11111111 11111111左移一位,符号位变成1,低位用0填充,所以结果位11111111 11111111 11111111 11111110,通过补码编码得出结果为-2
-
10000000 00000000 00000000 00000000左移一位,符号位变为0,代表正数,低位同样用0填充,结果位00000000 00000000 00000000 00000000,因此结果为0
右移
二进制右移动k位,丢弃低k位,并在高k位补最高位的值。其目的是为了负数的运算
-
01111111 11111111 11111111 11111111算术右移1位为00111111 11111111 11111111 11111111
-
10000000 00000000 00000000 00000000算术右移1位为11000000 00000000 00000000 00000000 可以看出,不论正负数,右移一位相当于除以2
无符号右移动
无符号右移,直接在高位补0
与或非异或运算
与就是全1是1,或是有1就1,异或是不同就1, 非就是取反
| 与(&) | 或(|) | 异或(^) | ||
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
Java中常见的位运算
- 返回最接近给定数据的2倍数
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
通过右移 1 2 4 8 16 让整个int 32位都覆盖到,或运算表示有1就1,所以上面操作获得了一个从当前数据往后都是1的二进制,然后再加一就全部为0进1位,就是最接近的两倍,开头的的减一是为了刚好2倍的时候也等于自身,因为参与运算的是比自己小1的数.
- 计算是不是余数 . (A % B == 0) 等价于 (A & (B-1) == 0)