【计算机组成原理】2、二进制原码反码补码、左移右移、进制转换,进制相减、内存地址偏移计算与容量计算

2023-11-20

一、二进制的原码、反码、补码

1、二进制最高位是符号位:0表示正数,1表示负数
2、正数的原码、反码、补码都是一样
3、负数的反码=原码符号位不变,其它位取反(0->1,1->0)
4、负数的补码=反码+1
5、0的反码、补码都是0
6、在计算机运算的时候都是以补码的方式来运算
7、原码是给人用的(比如想知道53的原码,则因为 53 = 32 + 16 + 4 + 1, 且53为正数, 所以53的原码为0011 0101,然后可推算出53的反码、53的补码)

1.1 二进制计算

2 & -3
        原码            反码        补码
2   =>  0000 0010   0000 0010   0000 0010
-3  =>  1000 0011   1111 1100   1111 1101
------------------------------------------------
                                0000 0000

                                反推原码也为0 (因为0的原码、反码、补码均为0)

2 | -3
        原码            反码        补码
2   =>  0000 0010   0000 0010   0000 0010
-3  =>  1000 0011   1111 1100   1111 1101
------------------------------------------------
                                1111 1111
                                
                                反推为原码:(因为补码运算结果首位为1所以补码为负数, 所以得到补码,再得到原码)
         原码       补码         运算结果
         1000 0001  1111 1110   1111 1111
         结果为: -1

2 ^ -2
        原码            反码        补码
2   =>  0000 0010   0000 0010   0000 0010
-2  =>  1000 0010   1111 1101   1111 1110
------------------------------------------------
                                1111 1100
                        
                                反推为原码:(因为补码运算结果首位为1所以补码为负数, 所以得到补码,再得到原码)
        反码         补码        运算结果
        1000 0100   1111 1011   1111 1100
        结果为: -4

2 ^ 4
        原码            反码        补码
2   =>  0000 0010   0000 0010   0000 0010
4   =>  0000 0100   0000 0100	0000 0100
------------------------------------------------
                                0000 0110
                        
                                反推为原码:(因为补码运算结果首位为0所以补码为正数, 而正数的原码、反码、补码均相同)
        反码         补码        运算结果
        0000 0110   0000 0110   0000 0110
        结果为: 6

func main() {
	a, b := 2, -3
	fmt.Printf("%4d原码: %08b\n", a, a)
	fmt.Printf("%4d原码: %08b\n", b, b)
	fmt.Printf("%4d原码: %08b\n", a&b, a&b)
	fmt.Printf("%4d原码: %08b\n", a|b, a|b)
	fmt.Printf("%4d原码: %08b\n", a^(-a), a^(-a))
	fmt.Printf("%4d原码: %08b\n", a^(2*a), a^(2*a))
}
   2原码: 00000010
  -3原码: -0000011
   0原码: 00000000
  -1原码: -0000001
  -4原码: -0000100
   6原码: 00000110

1.2 左移、右移

二进制左移一位,就是乘以2
二进制右移一位,就是除以2

  • 类型:
    • 算数右移(>>):移动所有位,同时保留原符号位
    • 逻辑右移(>>>):移动所有位,同时固定新符号位为 0
  • 编程语言:
    • Java、Python:>> 为算数右移,>>> 为逻辑右移
    • C、C++、go:只有 >>,若 signed 则算数右移,若 unsigned 则算数右移和逻辑右移等价

func main() {
	var a uint8 = 53
	fmt.Printf("%4d原码: %08b\n", a, a) 			//  53原码: 00110101
	fmt.Printf("%4d原码: %08b\n", a>>1, a>>1)		//  26原码: 00011010
	fmt.Printf("%4d原码: %08b\n", a>>2, a>>2)		//  13原码: 00001101
}
因为 53 = 32 + 16 + 4 + 1,53为正数, 所以53的原码为0011 0101
         原码            反码        补码
53   =>  0011 0101	 0011 0101   0011 0101
// 由53的补码 右移一位 得到26的补码, 进而算出26的反码, 进而算出26的补码
26   =>  0001 1010   0001 1010	 0001 1010 
// 由26的补码 右移一位 得到13的补码, 进而算出13的反码, 进而算出13的补码
13   =>  0000 1101   0000 1101	 0000 1101 



func main() {
	var a uint8 = -53
	fmt.Printf("%4d原码: %08b\n", a, a) 			//  -53原码: -0110101
	fmt.Printf("%4d原码: %08b\n", a>>1, a>>1)		//  -27原码: -0011011
	fmt.Printf("%4d原码: %08b\n", a>>2, a>>2)		//  -14原码: -0001110
}
因为 53 = 32 + 16 + 4 + 1,-53为负数, 所以-53的原码为1011 0101
而负数的反码=原码符号位不变,其它位取反(0->1,1->0), 所以-53的反码为1100 1010
而负数的补码=反码+1, 所以-53的补码为1100 1011
          原码            反码        补码
-53   =>  1011 0101	  1100 1010   1100 1011
// 由-53的补码 右移一位 得到-27的补码, 进而算出-27的反码, 进而算出-27的补码
-27   =>  						  1110 0101
// 由-27的补码 右移一位 得到-14的补码, 进而算出-14的反码, 进而算出-14的补码
-14   =>  						  1111 0010



func main() {
	var a int = 128
	fmt.Printf("%4d原码: %08b\n", a, a)       // 128原码: 10000000
	fmt.Printf("%4d原码: %08b\n", a>>1, a>>1) //  64原码: 01000000
	fmt.Printf("%4d原码: %08b\n", a>>2, a>>2) //  32原码: 00100000
}
因为 128 = 128 + 0,128 为正数, 所以128的原码为1000 0000
而正数的原码、反码、补码均相同,所以得到其反码、补码如下:
          原码            反码        补码
128   =>  1000 0000	  1000 0000   1000 0000
// 由128的补码,右移一位,得到补码0100 0000, 因为0100 0000为正数, 所以其反码和补码均为此数,换算为十进制的64
64    =>  0100 0000	  0100 0000   0100 0000
// 由64的补码,右移一位,得到补码0010 0000, 因为0010 0000为正数, 所以其反码和补码均为此数,换算为十进制的32
32    =>  0010 0000	  0010 0000   0010 0000



func main() {
	var a int8 = -128
	fmt.Printf("%4d原码: %08b\n", a, a)				// -128原码: -10000000
	fmt.Printf("%4d原码: %08b\n", a>>1, a>>1)		//  -64原码: -1000000
	fmt.Printf("%4d原码: %08b\n", a>>2, a>>2)		//  -32原码: -0100000
}
因为 128 = 128 + 0,-128 为负数, 所以-128的原码为1000 0000
而负数的反码=原码符号位不变,其它位取反(0->1,1->0), 所以-128的反码为0111 1111
而负数的补码=反码+1, 所以-128的补码为1000 0000
           原码            反码        补码
-128   =>  1000 0000	  0111 1111   1000 0000
// 由-128的补码,右移一位,得到补码1100 0000, 因为1100 0000为负数, 所以其反码为1011 1111, 所以其原码为1100 0000,换算为十进制的-64(即-1 * 64)
-64    =>  1100 0000	  1011 1111   1100 0000
// 由-64的补码, 右移一位,得到补码1110 0000, 因为1110 0000为负数, 所以其反码为1101 1111, 所以其原码为1010 0000,换算为十进制的-64(即-1 * 32)
-32    =>  1010 0000	  1101 1111   1110 0000

1.3 异或

“异或”操作的本质其实就是,所有数值和自身进行按位的“异或”操作之后都为 0。而且要通过“异或”操作得到 0,也必须通过两个相同的数值进行按位“异或”。这表明了两个数值按位“异或”结果为 0,是这两个数值相等的必要充分条件,可以作为判断两个变量是否相等的条件。

110100 ^ 100011 = 010110

二、进制转换

对于整数,有四种表示方式:
1)、二进制:0,1 ,满 2 进 1。在 golang 中,不能直接使用二进制来表示一个整数,它沿用了 c 的特点。
2)、十进制:0-9 ,满 10 进 1。
3)、八进制:0-7 ,满 8 进 1. 以数字 0 开头表示。
4)、十六进制:0-9 及 A-F,满 16 进 1. 以 0x 或 0X 开头表示。此处的 A-F 不区分大小写

简单的格式化进制输出的示例
package main
import "fmt"
func main() {
	var i int = 5; fmt.Printf("%b\n", i)   // 十进制格式化为二进制
	var j int = 011; fmt.Println("j=", j)  // 八进制格式化为十进制
	var k int = 0x11; fmt.Println("k=", k) // 十六进制格式化为十进制
}

2.1 二进制、八进制、十六进制转为十进制

// 二进制转为十进制
例如:1011
手动换算:1 * 2^(4-1) + 0 * 2^(3-1) + 1 * 2^(2-1) + 1 * 2^(1-1)
              8     +      0      +      2      +   1    =   11    
代码运算:
	ret, _ := strconv.ParseInt("1011", 2, 64) // 11


// 八进制转为十进制
例如:23
手动换算:2 * 8^(2-1) + 3 * 8^(1-1)
              16     +      3     =     19        
代码运算:
	ret, _ := strconv.ParseInt("23", 8, 64) // 19


// 十六进制转为十进制
例如:23
手动换算:2 * 16^(2-1) + 3 * 16^(1-1)
              32     +      3     =     35  
代码运算:
	ret, _ := strconv.ParseInt("23", 16, 64) // 35

2.2 十进制转为二进制、八进制、十六进制

// 十进制转为二进制
例如:56
手动换算:         余数,从下往上取值
	2 | 56   ...   0
	2 | 28   ...   0
	2 | 14   ...   0
	2 | 7    ...   1
	2 | 3    ...   1
	  1      ...   1
    111000
代码运算:
	ret := strconv.FormatInt(56, 2) // 111000


// 十进制转为八进制
例如:56
手动换算:         余数,从下往上取值
	8 | 56  ...  0
	  7     ...  7  //小于8,从下往上取值
    70
代码运算:
	ret := strconv.FormatInt(56, 8) // 70


// 十进制转为十六进制
例如:56
手动换算:         余数,从下往上取值
	16 | 56  ...   8
	   3     ...   3  //小于16,从下往上取值
    38
代码运算:
	ret := strconv.FormatInt(56, 16) // 38

2.3 二进制转为八进制、十进制、十六进制

// 二进制转为八进制
例如: 11010101
手动换算:每取3位,进行 4 2 1 按位的运算
    421=3  421=2   421=5
	011    010     101
	结果:0325
代码运算:
	// 2进制转为10进制
	base_10, _ := strconv.ParseInt("11010101", 2, 64) //213

	// 10进制转为8进制
	ret := fmt.Sprintf("%o", base_10) //325
	fmt.Println(ret)



// 二进制转为十六进制
因 $2^4=16$,所以每4个二进制位,可表示为1个十六进制。例如如下:
- 00001111(B) = F(H)0xF0x0F
- 11111111(B) = FF(H)0xFF
- 111100001111(B) = F0F(H)0xF0F
- 1111111100001111(B) = FF0F(H)0xFF0F

例如: 11010101
手动换算:每取4位,进行 8 4 2 1 按位的运算
        8421=13   8421=5
		1101      0101
		D5
代码运算:
	// 2进制转为10进制
	base_10, _ := strconv.ParseInt("11010101", 2, 64) //213

	// 10进制转为16进制
	ret := fmt.Sprintf("%X", base_10) //D5
	fmt.Println(ret)



// 二进制转为十进制
例如: 11010101
手动换算:
		// 先转为16进制
		每取4位,进行 8 4 2 1 按位的运算
        8421=13   8421=5
		1101      0101
		D5
		// 然后再由十六进制转为10进制
		D * (16^2-1) + 5 * (16^1-1) 
        13*16        + 5    =   213
代码运算:
	// 2进制转为10进制
	base_10, _ := strconv.ParseInt("11010101", 2, 64) //213

2.4 八进制、十进制、十六进制转为二进制

// 八进制为二进制
例如: 123
手动换算:采用:421,按位换算
	421    421   421 
	001    010   011
	  1     2      3
-----------------------
	123 ==> 001010011
代码运算:
	// 8进制转为2进制
	ret := fmt.Sprintf("%b", 0123)
	fmt.Println(ret) //1010011



// 十进制为二进制
例如: 123
手动换算:取2,取余数,从下往上取值
	2 | 123 ... 1
    2 |  61 ... 1
    2 |  30 ... 0
    2 |  15 ... 1
    2 |   7 ... 1
    2 |   3 ... 1
	2 |   1 ... 1
---------------------
    1111011
代码运算:
	// 10进制转为2进制
	ret := fmt.Sprintf("%b", 123) // 1111011
	fmt.Println(ret) 



// 十六进制为二进制1个十六进制 转换为 4个二进制位即可。例如如下:
- 0xF0F = 111100001111(B) = 0000111100001111(B)
- 0xFF0F = 1111111100001111(B)

例如: 123
手动换算:取4位,采用:8421,按位换算
	8421    8421   8421 
	0001    0010   0011
	1       2      3
-----------------------
	123 ==> 000100100011
代码运算:
	// 16进制转为2进制
	ret := fmt.Sprintf("%b", 0x123)
	fmt.Println(ret) //100100011

三、进制相减

3.1 十六进制

例如:0xA000 - 0x0800

  • 先从末尾开始减:最后两位的0x0000-0x0000 = 0x0000
  • 再倒数第三位相减 0x0000 - 0x0800,因为不够减,所以借位得到16(因为是十六进制相减),即 16 - 8 = 8
  • 在倒数第四位相减 0xA000 - 0x0000,因为已经被借走了一位,则变为 0x9000 - 0x0000 = 0x9000
    故最终答案为 0x9800

PS:类比二进制相减即容易理解了

四、内存地址偏移计算

在这里插入图片描述

例如:若一栋楼共100层,最底为第0层,则最顶为第99层。因为从0到(n-1)共有n个数,如下图:

在这里插入图片描述

4.1 根据首末地址,求存储容量

题目 如下:
在这里插入图片描述
如下图所示,首地址是 30000H,末地址是 AFFFFH。

因为题目说明内存以字节编制,则说明即每个地址是一个字节(即楼房的每层放了1个byte,即8个bit)。

题意问存储容量,即问能存储多少 bit。


题解如下:

  • 先求楼层数:

    • AFFFF(H) - 30000(H) + 1 = 7FFFF(H) + 1 = 80000(H)
    • 而 80000(H) = 1000-0000-0000-0000-0000(B) = 2^19,即 2 ^ 19 Bytes
      在这里插入图片描述
    • 2 1 0 = 1 K 2 ^ 10 = 1K 210=1K 2 2 0 = 1 M 2 ^ 20 = 1M 220=1M 2 3 0 = 1 G 2 ^ 30 = 1G 230=1G
    • 故 2 ^ 19 bytes = 2 9 2^9 29 * 2 10 2^{10} 210 = 2 9 K b y t e s 2^9 Kbytes 29Kbytes = 2 9 K B 2^9 KB 29KB = 512 KB
      在这里插入图片描述
  • 故最终答案为能存储 512 KB 的容量(如下图):

在这里插入图片描述

4.2 根据末地址 和 存储容量,求首地址

题目如下:

在这里插入图片描述


题解如下:

若题目未指明,则默认每个地址为 1个字节。

因为共 2KB 即 2K Bytes 即 2048 Bytes,需将 2000(十进制)转换为 十六进制。

因常识为 1024 = 2 10 2^{10} 210,故 2000 = 2 11 2^{11} 211 = 2后面1个0(B) = 1000-0000-0000(B) = 800(H) 即 0x800。详见下图:

在这里插入图片描述

而因公式为 存储空间 = 末地址 − 首地址 + 1 存储空间 = 末地址 - 首地址 + 1 存储空间=末地址首地址+1,故 首地址 = 末地址 − 存储空间 + 1 首地址 = 末地址 - 存储空间 + 1 首地址=末地址存储空间+1 = 9FFF(H) - 0800(H) + 1(B)= A000(H) - 0800(H) = 9800(H)。详见下图:

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【计算机组成原理】2、二进制原码反码补码、左移右移、进制转换,进制相减、内存地址偏移计算与容量计算 的相关文章

  • Ruby 脚本即服务

    嗯 标题说明了一切 我有一个 ruby 脚本 我想在我的 Linux 机器上作为一项服务 我可以启动和停止 运行 我找到了如何在 Windows 上执行此操作here https stackoverflow com questions 16
  • 当非特权用户运行 C/asm 程序时,会对 Linux 造成什么危害?

    我一直在考虑一种场景 让用户 可以是任何人 可能有恶意 提交在 Linux PC 我们称之为基准节点 上运行的代码 目标是为单线程例程创建一种自动化基准测试环境 假设一个网站向代理发布了一些代码 该代理将此代码交给基准节点 而基准节点仅与代
  • 如何在 Docker 容器中运行 Nginx 而不停止?

    我在 Docker 容器上安装了 Nginx 并尝试像这样运行它 docker run i t p 80 80 mydockerimage usr sbin nginx 问题是 Nginx 的工作方式是 初始进程立即生成一个 Nginx 主
  • 在 Linux 上的 makefile 和 Makefile 之间进行选择

    我想在一个目录中同时使用 Makefile 和 makefile 进行 make 默认情况下 它将执行makefile 我可以选择执行 Makefile 吗 提前致谢 最简单的选择是使用 f make f Makefile From man
  • 使用Visual C++进行Linux开发时是否可以直接使用linux文件夹/usr/include

    我尝试使用针对 ubuntu 16 04 VM 的 Visual C for Linux Development 插件 与虚拟机的连接以及本地文件传输到远程文件夹 home user projects projectx 均成功 但是 当我尝
  • 文件在 rpm 规范文件中列出两次

    我的规范文件的文件部分如下所示 files prefix htdocs config prefix htdocs share settings config inc php 现在 由于配置文件已经包含在 prefix htdocs 我收到警
  • 缺少 /var/lib/mysql/mysql.sock 文件

    我正在尝试访问 mysql 当我运行 mysql 命令时 我得到以下信息 root ip 10 229 65 166 tpdatabase 1 8 0 28356 mysql 错误 2002 HY000 无法连接到 通过socket本地My
  • 堆内存和Slab分配

    我很困惑heap and free list 我有几个问题 我对C中malloc的工作原理有自己的理解 如果我错了 请纠正我 堆内存是否被组织为数据的链表 空闲列表 块 堆内存和空闲列表有区别吗 我对存储分配的理解 有待改进 当我们调用ma
  • 如何在 C++ 中检查文件是否已被另一个应用程序打开?

    我知道 有is open C 中的函数 但我希望一个程序检查文件是否尚未被另一个应用程序打开 有没有办法使用标准库来做到这一点 编辑 在答案中澄清这是针对 Linux 应用程序的 不仅标准库没有这个功能 一般来说也是不可能的 你可以 在li
  • 如何在带有空格的目录上使用 find ?

    这里有一些奇怪的事情 mkdir p 1 2 3 4 touch 1 2 3 4 file jpg for f in find type f name jpg do echo f done 这返回 1 2 3 4 file jpg and
  • 在内核模块中执行shell命令

    是否可以在内核模块中执行shell命令 我知道我们可以在用户空间 C 代码中使用system子程序 我正在调试一个存在内存泄漏问题的内核模块 在无限循环中执行 insmod 和 rmmod module ko 后 8G RAM 的系统在几分
  • 自动将 Linux 文件名重命名为 Windows 中合法的新文件名

    我想将 linux 文件重命名为在 windows 中合法的文件名 它的长度不应超过允许的长度 并且不应包含 Windows 中不允许的字符 有时我将论文的标题复制到文件名 它们有特殊字符 例如 or 另外 从 pdf 中复制和粘贴标题时
  • 从 TestContainer 访问 Podman REST API

    我使用 Maven 3 和 Podman 1 8 0 开发一个 Java 应用程序 据我了解 Podman 提供了与 Docker 兼容的 REST API Java集成测试使用TestContainer框架 TestContainer 无
  • Android Studio无法更新

    我正在运行 Mint 14 Linux 并且在使用 Android Studio 时遇到了一个主要问题 运行更新功能时 所有软件包都会返回 访问被拒绝 状态 我怎样才能解决这个问题 如何授予下载和安装的权限 附加信息 studio sh 正
  • 将静态链接的 elf 二进制文件转换为动态链接的

    我有一个 elf 二进制文件 它已静态链接到 libc 我无权访问其 C 代码 我想使用 OpenOnload 库 它在用户空间中实现了套接字 因此与标准 libc 版本相比提供了更低的延迟 OpenOnload 实现标准套接字 api 并
  • 如何有效地将许多文件移动到新服务器?

    我正在更换托管提供商 需要将数百万个上传的文件传输到新服务器 所有文件都位于同一目录中 是的 你读对了 过去我这样做过 压缩源服务器中的所有文件 scp到新服务器的 zip Unzip Move directory to appropria
  • 如何将目录路径转换为唯一的数字标识符 (Linux/C++)?

    我正在研究获取目录 文件夹 并派生某种形式的唯一数字标识符的方法 我研究了 字符串到哈希 方法 但是 鸽子洞原理 http www codinghorror com blog 2007 12 hashtables pigeonholes a
  • 访问单个结构体成员是否会将整个结构体拉入缓存?

    我一直在读乌尔里希 德雷珀的书 每个程序员都应该了解的内存知识 http lwn net Articles 250967 并在部分3 3 2 缓存效果的测量 http lwn net Articles 252125 页面中间 它给我的印象是
  • 什么是 Linux 上易于使用的 C++ 分析器? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我需要分析一些在 Linux 上运行 C 的代码 你们能推荐一些分析器吗 使用 gprof 只需编译 pg标志 我认为 但不确定 你必须关
  • 如何保护Linux中的堆内存?

    我想将一块堆内存设置为只读 为此我尝试过memalign with mprotect 但是从 memalignment 中我能得到什么 memalign 从进程堆中分配内存 我想将堆的某些部分设为只读 有什么帮助吗 malloc gt mm

随机推荐