原码反码补码以及位运算

362 阅读4分钟

概述

首先需要知道的是,在二进制的世界里,计算机用第一位来表示符号 0 是正数 1 是负数。 计算机里面存储的都是 补码,然后我们来看相关的知识

机器数

机器数就是计算机里面真实的高低电平.

  1. 1 在计算机中 存储 0000 0001 (补码)
  2. -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, 非就是取反

与(&)或(|)异或(^)
00000
01011
10011
11110

Java中常见的位运算

  1. 返回最接近给定数据的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的数.

  1. 计算是不是余数 . (A % B == 0) 等价于 (A & (B-1) == 0)