很多编程初学者都可能有一个误区,数据在计算机中存储的数据都是以二进制的形式,比如-2的二进制1000 0010。这句话看似没有问题,但是他们认为的二进制实际上是数据的原码,事实上计算机中存储的数据是以补码形式存储的。
这里就需要引入原码、反码和补码的概念。
原码、反码、补码
原码:数据的二进制,直接计算的结果。
如:10(0000 1010) -10(1000 1010)
反码:顾名思义原码取反,但需要分类讨论。正数的反码就是原码,负数的反码是原码除符号位外,其他位按位求反。
如:10(0000 1010) -10(11110101)
补码:所有数据在内存中都是以补码形式存储,也需要分类讨论。正数的补码就是原码,负数的补码是反码+1,但不能改变符号位(-128)。
如:10(0000 1010) -10(11110110)
注意:负数是指有符号数,无符号数均为非负数,原码即是补码。
补码的计算
1)第一种方法
先判断是正数还是负数,正数的补码就是原码。
如果是负数,根据原码、反码、补码的关系,一步步求出补码。
- 求原码,转换成二进制
- 求反码,原码除符号位不动,其他按位求反
- 反码+1得到补码
同理,可以从补码计算出原码。同样的先判断正负,正数的原码就是补码。
负数的原码跟着步骤倒着求即可。
- 补码-1得到反码
- 反码符号位不变,其他位按位求反
- 原码转十进制
2)第二种方法
同样的,先判断是正数还是负数,正数的补码就是原码。
而负数的补码是通过 模减去负数的绝对值,将这个数转换成二进制即为补码。
这里引入一下模的概念,“模”是指一个计量系统的计数范围,如:时钟的计量范围是0~11,模=12。表示n位的计算机计量范围是 0-2的n次幂-1,模=2的n次幂.“模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。
[X]补 = 模-| X |
按照定义,-128,如下求补码。
[-128]补 = 256-|-128| = 128
写成二进制,就是:1000 0000。
反码的出现
在计算机中只有加法没有减法,1-1 其实是 1+(-1)。如果用原码计算1+(-1)计算出的结果等于-2,与实际情况不同。因此反码出现了,反码的出现就是为了解决1+(-1)这类问题的。
原码计算 1+(-1)
1 0000 0001
+ -1 1000 0001
-------------------------------
1000 0010 => -2
反码计算 1+(-1)
1 0000 0001
+ -1 1111 1110
-------------------------------
1111 1111 => 反码
1000 0000 => 结果是-0,负数的原码是反码除符号位各位取反
补码的出现
反码的出现解决了1+(-1)的问题,尽管结果没问题,但是出现了-0这个数,也就是0这个数占了两个二进制(+0和-0),因此出现了补码来解决这个问题。
补码计算 1+(-1)
1 0000 0001
+ -1 1111 1111
-------------------------------
0000 0000 => 补码
0000 0000 => 结果是+0,正数的反码是补码
0000 0000 => 结果是+0,正数的原码是反码
补码的出现解决了-0的问题,使得-0的位置空了出来,于是约定 -0 = -128
位运算符
位运算符(将数据转换成二进制补码,对每一位二进制运算):& | ~ ^ >> <<
A & B 按位相与
10101101 0xAD
& 01101110 0x6E
-------------
00101100 0x2C
A | B 按位相或
10101101 0xAD
| 01101110 0x6E
-------------
11101111 0xEF
~A 按位求反
~ 10101101 0xAD
-----------
01010010 0x52
A ^ B 按位异或,相同为0,不同为1
10101101 0xAD
& 01101110 0x6E
-------------
11000011 0xC3
A << n 把A的补码向左移动n位,左边超出范围丢弃,右边补零 (相当于乘上2^n倍)
10101101 << 4 0xAD
--------------
11010000 0xD0
A >> n 把A的补码向右移动n位, 右边丢弃,左边补符号位
10101101 >> 4 0xAD
--------------
00001010 0xA
结语
希望这篇文章对你有所帮助