天天看点

原码反码补码详析及移位原码、反码和补码的详析 及 移位

原码、反码和补码的详析 及 移位

文章目录

  • 原码、反码和补码的详析 及 移位
    • 一、原码、反码和补码的定义与 表现形式
      • 1.原码
      • 2.反码
      • 3.补码
      • 4.用表格总结,便于记忆!!
    • 二、为何使用原码、补码和反码
      • 1.原码的加法
      • 2.反码的加法(循环进位)
      • 3.补码的加法(舍弃进位)
      • 4. 32位int的表示范围 [-2147483648, 2147483647]
    • 三、[反码的数学理论深入][2]
      • 3.1取模同余
      • 3.2反码的同余
      • 3.3补码的意义所在
    • 四、二进制运算
      • 与或异或
      • 左移
      • 右移
      • 总结:
    • 参考文章

一、原码、反码和补码的定义与 表现形式

原码反码与补码是机器存储一个数字的具体编码方式。

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.反码的加法(循环进位)

反码的运算规则:

  1. 反码运算时,其符号位与数值一起参加运算。
  2. 反码的符号位相加后,如果有进位出现,则要把它送回到最低位去相加(循环进位)。
  3. 用反码运算,其运算结果亦为反码。在转换为真值时,若符号位为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点,该怎么调整钟表,下面有两种方法:

  1. 时针往回拨3小时,5-3=2
  2. 时针往前拨9小时,5+9=14, 14 mod 12=2

mod 指的是取模操作,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位将被丢弃,左边补位的时候,要分情况。

  1. unsigned 无符号,补0
  2. 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