专栏名称: 嵌入式微处理器
关注这个时代最火的嵌入式微处理器,你想知道的都在这里。
目录
相关文章推荐
易简财经  ·  DeepSeek带飞阿里股价,阿里人连夜看豪宅 ·  23 小时前  
晨枫老苑  ·  战斗机换发是否需要重新研发发动机数控 ·  2 天前  
晨枫老苑  ·  战斗机换发是否需要重新研发发动机数控 ·  2 天前  
李楠或kkk  ·  it 行业有一个方法论叫 ... ·  2 天前  
众视AsiaOTT  ·  孙忠怀:腾讯视频如何重新定义“好剧” ·  3 天前  
51好读  ›  专栏  ›  嵌入式微处理器

一文弄懂二进制表示和补码计算的来龙去脉

嵌入式微处理器  · 公众号  · 科技自媒体  · 2024-08-29 13:02

主要观点总结

本文介绍了计算机系统中的二进制系统,包括从十进制到二进制的转换、从十进制加法到二进制加法的演变,以及负数计算转换成正数计算的过程。文章还探讨了原码、反码和补码的概念,以及如何表示0的问题。最后,文章总结了二进制系统的表示和计算方式,并呼吁读者共同学习成长。文章中提到了二进制数的范围、表示方法、计算过程以及补码的计算和含义。

关键观点总结

关键观点1: 十进制到二进制的转换

介绍了如何将十进制数转换为二进制数,包括每一个数位的含义和计算方法。

关键观点2: 二进制加法

讲解了二进制数的加法运算规则,包括相同数位上的数字相加和进位规则。

关键观点3: 负数计算转换成正数计算

介绍了如何将负数计算转换成正数计算,包括原码、反码和补码的概念,以及如何解决0的表示问题。

关键观点4: 补码的计算和含义

详细讲解了补码的计算方法,包括正数和负数的补码,以及补码在二进制计算中的应用。

关键观点5: 文章总结

文章最后总结了二进制系统的表示和计算方式,并强调了技术学习和共同成长的重要性。


正文

本文目录:

  • 一、前言

  • 二、从十进制到二进制

    • 1. 十进制

    • 2. 二进制

    • 3. 十六进制

    • 4. 任意进制

  • 三、从十进制加法到二进制加法

    • 1. 十进制加法

    • 2. 二进制加法

    • 3. 十六进制加法

  • 四、把负数计算转换成正数计算

    • 1. 原码

    • 2. 把负数计算变成正数计算

    • 3. 新问题:如何表示0?

    • 4. 补码的计算:同余定理

  • 五、总结

一、前言

计算机最喜欢的数字就是 0 和 1 ,在 CPU 的世界中,它只认识这两个数字,即使是强大的 操作系统 ,也都是由 0 和 1 组成的。

作为一名软件开发者,入门学习的内容可能就是认识这 2 个既简单、又强大的数字。但是大部分人,对于 二进制、二进制计算、原码、反码以及补码 的认识,仍处于机械的 强制记忆阶段 。尤其是对一些编码和计算,仍然处于模糊的认识阶段,例如:

  1. CPU 是如何表示负数的?

  2. 为什么补码可以用来表示负数?

  3. 一个 8 位的二进制数,最小值为什么是 -128,而不是 -127?

  4. CPU 中的加法器,为什么可以连同符号位一起运算?

这篇文章我们就来聊聊这个最最基础的内容,帮助你来理解 二进制计算 的相关内容,看完这篇文章之后,不仅知其然,更能知其所以然!

PS: 这里有点高调了,最终的所以然部分,应该涉及到 数学证明 这一层次了,本文并不会涉及到求证过程。

二、从十进制到二进制

1. 十进制

作为数学计算能力强大的中国,10 以内的加减法,应该是在幼儿园阶段就完成了。如果你不属于这个范围,说明你上的是假幼儿园。

我们来快速复习一下关于 十进制 运算的一些基本知识:

  1. 每一个数位上包括的数字为 0 到 9;

  2. 每一个数位上的数,是它右侧数位的 10 倍;

  3. 两个数相加时,相同数位上的数相加之和如果大于等于 10,就向前进 1 位,即:满十进一;

具体来看就是:

  1. 从右数第一个位数(个位)上的数字代表多少个 1;

  2. 从右数第二个位数(十位)上的数字代表多少个 10;

  3. 从右数第三个位数(百位)上的数字代表多少个 100;

  4. 从右数第四个位数(千位)上的数字代表多少个 1000;

十进制的数,可以使用后缀字母 D 来表示,也可以省略。例如:十进制的 1234 这个数字,个位上的数是 4, 十位上的数是 3, 百位上的数是 2,千位上的数是 1(一般是从 最右侧 的个位说起),每一个数位上的数比它右侧大十倍。如下图:

十进制数据,也称作 基于十的表示法

2. 二进制

那么对于 二进制 呢?直接 套用 上面 十进制 的概念,然后 把 10 换成 2 即可(目前先忽略符号位):

  1. 每一个数位上包括的数字为 0 和 1;

  2. 每一个数位上的数,是它右侧数位的 2 倍;

  3. 两个数相加时,相同数位上的数相加之和如果大于等于 2,就向前进 1 位,即:满二进一;

具体来看就是:

  1. 从右数第一个位数上的数字代表多少个 1;

  2. 从右数第二个位数上的数字代表多少个 2;

  3. 从右数第三个位数上的数字代表多少个 4;

  4. 从右数第四个位数上的数字代表多少个 8;

记住几个重点:二进制数中只包含 0 和 1 两个数字,在相加时 满二进一

在十进制中,每一个数位我们给它进行了专门的命名(个位、十位、百位...),但是二进制没有类似的命名。

二进制的数,使用后缀字母 B 来表示,例如:二进制的 1111B 这个数字,用图来表示权重如下:

换算成 十进制 数就是 15(1 * 8 + 1 * 4 + 1 * 2 + 1 * 1 = 15)。

在二进制中,每一位称为一个 比特(bit) ,如果用 8 个 bit 来表示一个二进制数, 最小值 是 0000_00000, 最大值 是 1111_1111;

如果用 16 个 bit 来表示一个二进制数, 最小值 是 0000_0000_0000_0000, 最大值 是 1111_1111_1111_1111。(为了便于观察,每 4 个 bit 之间,加上了分隔符)

在早期的计算机中,8 位的处理器很常见,于是就给它一个专门的名字: 字节(Byte) 。16 位的二进制数就是 2 个字节,也称作: 字(Word)

3. 扩展到十六进制

原理还是相同的:直接 把十进制中的 10 换成 16 即可:

  1. 每一个数位上包括的数字为 0 到 9,A 到 F;

  2. 每一个数位上的数,是它右侧数位的 16 倍;

  3. 两个数相加时,相同数位上的数相加之和如果大于等于 16,就向前进 1 位,即:满十六进一;

具体来看就是:

  1. 从右数第一个位数上的数字代表多少个 1;

  2. 从右数第二个位数上的数字代表多少个 16;

  3. 从右数第三个位数上的数字代表多少个 256;

  4. 从右数第四个位数上的数字代表多少个 4096;

在十六进制中,需要 十六个 数字来表示 0 到 15 这些数字,0 到 9 比较好处理,但是从 10 到 15 ,我们就需要找一些记号来表示,于是人们就想到用 A,B,C,D,E,F 这几个字母来分别表示 10 到 15 这个 6 个数字。

十六进制数据,使用后缀字母 H 来表示,有些场合也可以使用 前缀 0x 来表示,本质上没有区别。例如:十六进制数字 1A2BH(或者写作 0x1A2B),每一个数位上的权重如图:

换算成 十进制 数就是 6699(1 * 4096 + 10 * 256 + 2 * 16 + 11 * 1 = 6699)。

4. 扩展到任意进制

原理仍然相同:直接把 十进制中的 10 换成目标进制 ,例如 5 进制

  1. 每一个数位上包括的数字为 0 到 4;

  2. 每一个数位上的数,是它右侧数位的 5 倍;

  3. 两个数相加时,相同数位上的数相加之和如果大于等于 5,就向前进 1 位,即:满五进一;

具体来看就是:

  1. 从右数第一个位数上的数字代表多少个 1;

  2. 从右数第二个位数上的数字代表多少个 5;

  3. 从右数第三个位数上的数字代表多少个 25;

  4. 从右数第四个位数上的数字代表多少个 125;

再看一个图加深印象:

三、从十进制加法到二进制加法

1. 十进制加法

这个就不必多说了,规则只有 2 条:

  1. 两个数,相同数位上的数字进行相加;

  2. 每一个数位上的相加结果,满十进一;

例如:

个位上 :4 + 8,结果是 12,但是十进制中没有 12 这个数字,因此向左侧的高位 进1 ,个位就剩下:12 - 10 = 2。

十位上 :7 + 2,再加上进位 1,结果是 10,但是十进制中没有 10 这个数字,因此向左侧的高位 进1 ,十位变成:10 - 10 = 0。

百位上 :1 加上进位 1,结果是 2。

2. 二进制加法

第 0 位 :0 + 0 结果为 0;

第 1 位 :1 + 0 结果为 1;

第 2 位 :1 + 1 结果为 2,但是二进制中 没有 2 这个数字,因此需要向左侧的高位 进 1 ,于是第 2 位上就剩下 2 - 2 = 0。

第 3 位 :1 + 1 等于 2,再加上进位 1,结果就是 3,但是二进制中 没有 3 这个数字,因此需要向左侧的高位 进 1 ,于是第 3 位上就剩下 3 - 2 = 1。

第 4,5,6,7位计算均是如此。

3. 十六进制加法

第 0 位 :E + C,结果为 26,但是十六进制中 没有 26 这个数字,因此需要向左侧的高位 进 1 ,于是第 0 位就剩下 26 - 16 = A。

第 1 位 :A + 1 等于 B,再加上进位 1,结果就是 C,十六机制中 有这个数字

四、把负数计算转换成正数计算

1. 原码

原码(true form) 是一种计算机中对数字的二进制 定点表示方法 。原码表示法在数值前面增加了一位 符号位 (即最高位为符号位):正数该位为0,负数该位为1(0有两种表示:+0和-0),其余位表示数值的大小。

例如,用 8 个 bit (8 位二进制数)来表示一个数,+11 的原码为 0000_1011,-11 的原码就是 1000_1011。

2. 把负数计算变成正数计算

我们都知道,CPU 中有 加法器 ,好像从来没有听说过 “减法器” 。例如计算 5 + 8,转换成二进制来计算:

再来计算一下减法:5 - 8,对于 CPU 来说,只会计算 5 + 8, 但是 不会 计算 5 - 8。

但是可以转换一下思路,把减法变成加法 5 + (-8) ,这样不就可以计算了吗?于是计算机先驱者就发明了反码:

  1. 正数的反码:保持原码不变;

  2. 负数的反码:原码中符号位不变,其余全部取反(-8 的原码是 1000_1000,反码就是:1111_0111);

于是 5 + (-8)的计算过程就是:

此时, 就完美解决了减法问题 ,那么乘法(多加几次)、除法(多减几次)问题也就跟着解决了。至于如何从数学的角度来证明,那就要问那些数学家了!

3. 新问题:如何表示0?

我们现在可以小结一下 反码 的表示范围(记住:第一位是符号位):

  1. 正数的表示范围:0000_0000 ~ 0111_1111,也就是十进制的 +0 ~ +127 这 128 个数;

  2. 负数的表示范围:1000_0000 ~ 1111_1111,也就是十进制的 -127 ~ -0 这 128 个数;

有没有发现问题:怎么存在 +0 和 -0 这两个数?而且他们的编码还不一样:+0 对应 0000_0000,-0 对应 1111_1111。

CPU 虽然就是一个傻瓜,让它干啥就干啥,但是 CPU 最不能容忍的就是 不确定性 !我们都知道 +0 == -0 == 0 ,它们是 同一个 数字,但是在二进制编码中,居然有两个编码来表示同一个数。

伟大的计算机先驱者又做了这样一个 决定 正数保持不变,负数整体减 1

也就是说: 符号位不变,值整体加1 ,如下:

这样就 成功解决了 -0、+0 的问题!

现在 一个 8 位的二进制就可以表示的范围是: -128 ~ 127 ,并且中间没有任何重复、遗漏的数字。

既然每一个二进制表示的值 发生了变化 ,那么继续称之为 反码 不准确 了,此时给它们一个新的称呼: 补码 ,也就是说:上图就变成了这样:

小结一下补码的定义:

  1. 正数的补码:保持原码不变;

  2. 负数的补码:原码中符号位不变,其余先全部取反,然后再加1(例如:-8 的原码是 1000_1000,补码就是 1111_1000);

此时,我们仅仅是解决了二级制编码的表示问题,那么: 补码能直接参与运算吗?运算结果会出现什么问题?

4. 补码的计算

我们先看一下这个问题:假设现在时间是 1 点整,但是你的手表进水了,它显示的是 3 点整,现在你怎么把时间调整到 1 点的位置?

方法1:把时针逆时针拨动 2 个小时(3 - 2 = 1);







请到「今天看啥」查看全文