位:二进制简称“位”,是二进制计数系统中表示小于 2 的整数符号,一般用 1 或 0 表示,是具有相等概率的两种状态中的一种。二进制的位数可表示一个机器字的字长,一个二进制位包含的信息量称为 1 bit。(摘自百度百科)
位运算符用来对二进制位进行操作,Java中提供了如下所示的位运算符(操作数只能为整型和字符型数据):
& 按位与
| 按位或
^ 按位异或
~ 按位取反
除 ~ 以外,其余均为二元运算符。
一个字节 = 8bit(位)
一个 int 类型的数值占 32bit(位)
二进制换算示例
int i = 123;
10 进制 123 转为二进制后等于:1111011
完整补位后:
00000000 00000000 00000000 01111011
tips:全文使用的二进制为 32 位。
原码、反码和补码二进制的最高位为符号位,1 表示负数,0 表示整数,其余位表示数的绝对值。
123 转为二进制补齐后为:00000000 00000000 00000000 01111011,这是 123 的原码。
负数 -123 的原码:100000000 00000000 00000000 01111011。
其中首位是符号位,0代表正数,1代表负数。
反码:正数的反码和原码相同,负数的反码是保留其符号位不变,其他位取反,(0 变 1,1 变 0)。
补码:正数的补码和原码相同,负数的补码为原码除符号位外取反 +1。
注意:整数在计算机中,是以补码的形式存储的。
正数对比
123 的原码:00000000 00000000 00000000 01111011
123 的反码:00000000 00000000 00000000 01111011
123 的补码:00000000 00000000 00000000 01111011
负数对比
-123 的原码:10000000 00000000 00000000 01111011
-123 的反码:11111111 11111111 11111111 10000100
-123 的补码:11111111 11111111 11111111 10000101
已知补码求原码
最高位如果是 1 的话(负数),那么除了最高位之外的取反,然后加 1 得到原码。
最高位如果是 0 的话(正数), 不变,正数的补码就是它的原码。
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。(摘自百度百科)
打个比方:2-1是怎么计算的?
二进制计算规则采用11得1,10得1,00得0 逢2进1
计算公式:2-1=2+(-1);
2 的补码:00000010
-1 的补码:11111111
-1 的原码:10000010
-1 的反码:11111101
结果 0 00000001,最高位溢出(0)丢弃, 2-1 = 1。
关于原码、反码和补码的详细解释,可看这篇:
https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html
为何要使用原码, 反码和补码在开始深入学习前, 我的学习建议是先"死记硬背"上面的原码, 反码和补码的表示方式以及计算方法.
现在我们知道了计算机可以有三种编码方式表示一个数. 对于正数因为三种编码方式的结果都相同:
[+1] = [00000001]原 = [00000001]反 = [00000001]补
所以不需要过多解释. 但是对于负数:
[-1] = [10000001]原 = [11111110]反 = [11111111]补
可见原码, 反码和补码是完全不同的. 既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢?
首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头). 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.
于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:
计算十进制的表达式: 1-1=0
1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数.
为了解决原码做减法的问题, 出现了反码:
计算十进制的表达式: 1-1=0
1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在"0"这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.
于是补码的出现, 解决了0的符号以及两个编码的问题:
1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128:
(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补