原码反码与补码是机器存储一个数字的具体编码方式。
1.原码第一位是符号位,其余位表示 数值的绝对值。
对于1Byte 来说:
+1 的原码:0000 0001, 0x01
-1 的原码:1000 0001, 0x81
第一位是符号位,1代表负数,0代表正数。
1Byte所能表示的整数范围为: [1111 1111, 0111 1111],即 [-127, 127]。
2.反码对于正数,其反码=原码
对于负数,其反码=除符号位外的原码位取反。
+1 的反码:0000 0001, 0x01
-1 的反码:1111 1110, 0xFE
3.补码
对于正数,其补码=原码
对于负数,其补码= 反码+1
+1 的补码:0000 0001, 0x01
-1 的补码:1111 1111, 0xFF
4.用表格总结,便于记忆!!
数值(真值) | 原码 机器数 | 反码机器数 | 补码机器数 |
---|---|---|---|
正数 | 第一位0,其余为数值绝对值 | 原码 | 原码 |
负数 | 第一位1,其余为数值绝对值 | 原码除符号位取反 | 反码+1 |
举例:- 1 | 0000 0001 | 0000 0001 | 0000 0001 |
举例:-1 | 1000 0001 | 1111 1110 | 1111 1111 |
原码对于人来说,非常直观,通过第一位判断正负后,通过其他位来计算绝对值。但是对于机器来说,将第一位和其他位区分开来,在程序设计和电路设计上会非常复杂。能否将符号位统一进计算方式中呢?
实际上,机器计算,只有加法而没有减法,因为减去一个数等于加上该数的负数。
所以,能否将符号位参与进位运算,且只保留加法运算呢?
1.原码的加法十进制:1 + (-1) = 0
二进制:0000 0001[原] + 1000 0001[原] = 1000 0010[原] = -2
让符号位参与运算后,发现原码的计算结果有问题!与十进制的计算方式不对应。
为了解决这个问题,来看看反码的加法。
2.反码的加法(循环进位)反码的运算规则:
反码运算时,其符号位与数值一起参加运算。
反码的符号位相加后,如果有进位出现,则要把它送回到最低位去相加(循环进位)。
用反码运算,其运算结果亦为反码。在转换为真值时,若符号位为0,数位不变;若符号位为1,应将结果求反才是其真值。(这里求反不包括符号位)
十进制:1 + (-1) = 0
二进制:0000 0001[反] + 1111 1110[反] = 1111 1111[反] = 1000 0000[原] = -0
数值是对了,但是是 -0 ,0的话,原则上是没有正负区分的,0加上符号之后也没有什么意义。
除此之外,两种编码 0000 0001[原] + 1000 0000[原] 都表示了0,造成了浪费。
十进制:-1 + (-127) = -128, -128超出了原码(反码)的表达范围,溢出变为126,不对
二进制:1111 1110[反] + 1000 0000[反] = 0111 1111[反] = 0111 1111[原] = 127
3.补码的加法(舍弃进位)补码的运算规则:
两个机器数相加的补码可以先通过分别对两个机器数求补码,然后再相加得到,在采用补码形式表示时,进行加法运算可以把符号位和数值位一起进行运算(若符号位有进位,导致了溢出,则直接舍弃,注意这里和反码加法计算的区别),结果为两数之和的补码形式。
补码的出现,解决了0的多编码问题。
十进制:1 + (-1) = 0
二进制:0000 0001[补] + 1111 1111[补] = 0000 0000[补] = 0000 0000[原] = 0
那多出来的 1000 0000[补] 来表示谁呢?答案是 -128
-127 的 原码、反码和补码分别是:1111 1111[原] 1000 0000[反] 1000 0001[补]
十进制:-1 + (-127) = -128
二进制:1111 1111[补] + 1000 0001[补] = 1000 0000[补]
在这里,用1000 0000[补]来表示-128 的补码,虽然按照前一节的定义, 1000 0000[补] 对应的原码是 0000 0000[原],但这里不这么对应。所以,-128 只有补码,没有原码和反码。
考虑符号位时,原码的表示范围为[-127, 127], 采用补码后,将 -0 变为表示 -128,这样补码的表示范围就是 [-128, 127],多保存了一个最小值。
4. 32位int的表示范围 [-2147483648, 2147483647]对于具有32bit位的int,
其最小值为:-2147483648 = -2^31,其补码为 0x8000 0000
其最大值为:-2147483647=2^31-1,其补码为 0x7FFF FFFF
对-2147483648取负值时,按理论应该是2147483648,但超过int能表达的最大正值,相当于2147283647+1=0111…1111+0000…0001=1000…0000=-2147483648(按补码理解)。也就是说对-2147483648取负仍然是-2147483648。
对-2147483648-1时,相当于1000…0000+1111…1111(-1的补码)=0111…1111(溢出后)=2147483647(int的最大正值)
三、反码的数学理论深入 3.1取模同余以时针钟表为例,时间是12进制,如果当前是5点,希望设置为2点,该怎么调整钟表,下面有两种方法:
时针往回拨3小时,5-3=2 时针往前拨9小时,5+9=14, 14 mod 12=2mod 指的是取模操作,14针对12的余数是2。
可以看到时针往回拨的减法操作可以用往前拨的加法操作替代。
同余的概念:
两个整数a, b,若他们除以整数m的余数相同,则成它俩对于模 m 同余。
对于时钟,3mod12=3, 15mod12=3。 3和15对于模12同余。
正数取余概念很容易,那么负数怎么取模呢?
负数的取模:
x mod y = x - y * floor( x / y )
-3 mod 2 = -3 - 2 * floor(-3/2) = -3 - 2 * (-2) = -3 +4 = 1
-5 mod 12 = -5 - 12 * floor(-5/12) = -5 - 12 * (-1) = 12- 5 = 7
-3 mod 12 = -3 - 12 * floor(-3/12) = -3 - 12 * (-1) = 12 -3 = 9
因此:减法转加法,相当于 找到 负数 的同余 正整数。
对于时针问题,5-3=2, 5+9=14mod12=2
-3 mod 12 = 9。 -3 和 9 是同余!
3.2反码的同余下面看看反码和同余有什么关系。
拿 2 - 1来看,反码符号位参与运算,符号位进位后,循环加到最末一位:
2 - 1 = 2 + (-1) = [0000 0010]原 + [1000 0001]原 = [0000 0010]反 + [1111 1110]反 = [0000 0001]反 = 1
反码的计算结果是正确的。
在这里,关注的是 [0000 0010]反 + [1111 1110]反 ,因为正数的反码还是原码,如果在这里,将[1111 1110]反 除符号位之外的位看做是原码,则 -1 的反码表示的就是 126(除符号位)。
恰恰,126 mod 127 = 126 -1 mod 127 = 126
126 和 -1 同余,所以 2-1 和 2+126的余数是相同的。 都是正确结果1。
所以一个数的反码,相当于对于一个模的同余数,该模就是二进制所能表示的最大值。
像钟表一样,翻转一圈后找到正确的数值。
3.3补码的意义所在既然反码已经实现了减法变加法,那补码的意义何在呢?上一节说了,利用补码,可以增加一个数的表达 -128,同时0的编码方式也只有一种。
补码是反码加1。反码加1之后,为何还是可以得到正确的结果?
再来看 2 -1 的情况:
2 - 1 = 2 + (-1) = [0000 0010]原 + [1000 0001]原 = [0000 0010]反 + [1111 1110]反
= [0000 0010]补 + [1111 1111]补 = [0000 0001]补 = 1
在这里,如果把 -1 的 [1111 1111]补 看成是 原码(除符号位),则 表示 127。
其实,补码就是在反码的基础上,增加了 模 的值。
在反码中,模为127,在补码中模为128。
用补码表示的运算结果最小值和最大值应该是[-128, 128],但是由于0的特殊情况, 没有办法表示128, 所以补码的取值范围是[-128, 127]。
四、二进制运算 与或异或与 & | 或 | | 异或 ^ |
---|---|---|
0&0=0 | 0|0=0 | 0^0=0 |
0&1=0 | 0|1=1 | 0^1=1 |
1&0=0 | 1|0=1 | 1^0=1 |
1&1=1 | 1|1=1 | 1^1=0 |
m<<n,表示把m左移n位。最左边的n位将被丢弃,右边补上n个0;
00001010<<2 = 00101000
10001010<<3 = 01010000
右移m>>n,表示把m右移n位。右移的时候,右边的n位将被丢弃,左边补位的时候,要分情况。
unsigned 无符号,补0 signed,有符号,用符号位补。如果原来是正数,就补0,原来是负数,就补1.00001010>>2 = 00000010
10001010>>3 = 11110001
总结:左移时总是移位和补零;
右移时无符号数是移位和补零,此时称为逻辑右移;
而有符号数大多数情况下是移位和补最左边的位(也就是补最高有效位),移几位就补几位,此时称为算术右移。
参考文章 https://www.cnblogs.com/huangjianwu/p/4549085.html https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html https://blog.csdn.net/qq_41376345/article/details/80536654?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task