mirror of
https://github.com/ClovertaTheTrilobita/SanYeCao-blog.git
synced 2026-10-25 21:43:53 +00:00
added draft
This commit is contained in:
parent
ec0acdec59
commit
1dc17dfc8c
1 changed files with 210 additions and 0 deletions
210
src/blog/zh/arithmetic-in-computer.md
Normal file
210
src/blog/zh/arithmetic-in-computer.md
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
---
|
||||||
|
title: "如何教会计算机算术??加减篇"
|
||||||
|
pubDate: 2026-08-04
|
||||||
|
description: '欢迎来到琪露诺的奇妙数学教室,让我们从……加减法开始吧!'
|
||||||
|
author: "三叶"
|
||||||
|
image:
|
||||||
|
url: "https://files.seeusercontent.com/2026/08/04/fkE6/pasted-image-1785836458506.webp"
|
||||||
|
alt: "bintree"
|
||||||
|
tags: ["学习笔记", "计算机组成原理"]
|
||||||
|
---
|
||||||
|
|
||||||
|
1+1等于几?
|
||||||
|
|
||||||
|
这个问题困扰了人们很多年。
|
||||||
|
|
||||||
|
有人说,一加一等于1,因为两个麦芽糖黏在一起还是一个麦芽糖。
|
||||||
|
|
||||||
|
有人说,一加一等于2,因为双击`计算器.exe`,输入1+1,输出结果是2。
|
||||||
|
|
||||||
|
有人说……
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
……我编不下去了。
|
||||||
|
|
||||||
|
那么各位小朋友大朋友早上中午晚上好。今天我们要来教会一台计算机如何进行加减法。
|
||||||
|
|
||||||
|
就是这么简单。
|
||||||
|
|
||||||
|
咦?其实不简单吗?
|
||||||
|
|
||||||
|
## 零、让我们从零开始
|
||||||
|
|
||||||
|
假设你的老妈在厨房大吼:“崽啊,快来吃饭!菜都要凉辣!”但是你此时没有办法迅速赶到餐桌,因为你正在忙着偷偷用老妈的支付宝购买*那个游戏6*!你的手边只有餐厅吊灯的神秘开关,你要怎么像老妈传达信息呢?
|
||||||
|
|
||||||
|
很简单!控制灯的亮起即可!我们将点亮的灯泡视为`True`(或者更简单地:`1`),熄灭的灯泡视为`False`(或者`0`),这样,你就可以高效地传递信息!
|
||||||
|
|
||||||
|
随后你毅然决然地关闭了餐厅的灯,坏结局,你的老妈并不知道高深莫测的计算机组成原理,从她的视角来看……
|
||||||
|
|
||||||
|
<i>小崽子又欠揍了(抄起衣服架子)。</i>
|
||||||
|
|
||||||
|
怎么办,世界末日就在眼前!但是别慌,因为老妈还有10秒才会破门而入,在这么充裕的时间内足以我们开启下一章节了。
|
||||||
|
|
||||||
|
## 一、数字在计算机中的表达
|
||||||
|
|
||||||
|
### 1. 二进制
|
||||||
|
|
||||||
|
现在,想象你手中拿的是一根电线,没错,就是连着餐厅吊灯的那条电线,回想一下,你是怎么通过电线进行信号传输的呢?
|
||||||
|
|
||||||
|
那就是:通电代表`1`,未通电代表`0`。
|
||||||
|
|
||||||
|
但是这只能表达两种状态啊,这个世界不是非黑即白的,如果我们需要表达四种状态怎么办?
|
||||||
|
|
||||||
|
简单,再加上一根电线。
|
||||||
|
|
||||||
|
现在我们可以通过控制两根电线来表达`00`,`01`,`10`,`11`这四种状态,那给它们起个编号吧!
|
||||||
|
|
||||||
|
`00`代表`0`;`01`代表`1`;`10`代表`01+01`,进一位,是`2`;`11`就是`10+01`是`3`;
|
||||||
|
|
||||||
|
没错,这就是**二进制**表示数据。
|
||||||
|
|
||||||
|
在计算机中,我们普遍使用二进制来计算基础运算,这其实是个很显而易见的道理,如果使用十进制,我们需要针对十种不同的状态设计十个不同的电路,而二进制就很简单了,用一根简单的电线,通电代表1,没通电代表0。
|
||||||
|
|
||||||
|
#### 二进制的转换
|
||||||
|
|
||||||
|
那么假如给你一串随机的**二进制数**:`010110`,我们怎么知道它对应的是多少呢?
|
||||||
|
|
||||||
|
我们先来看看十进制数。
|
||||||
|
|
||||||
|
随便取一个十进制数:`21367`,它可以很直观地被拆成
|
||||||
|
$$
|
||||||
|
2\times10^4+1\times10^3+3\times10^2+6\times10^1+7\times10^0=21367
|
||||||
|
$$
|
||||||
|
那么很直观地我们能想到,对于<b>r进制</b>的数`21367`,我们可以表达成
|
||||||
|
$$
|
||||||
|
2\times r^4+1\times r^3+3\times r^2+6\times r^1+7\times r^0=21367
|
||||||
|
$$
|
||||||
|
更通用地,对于<b>r进制的数</b>$K_nK_{n-1}K_{n-2}...K_1K_0$,可以表达为
|
||||||
|
$$
|
||||||
|
K_nr^n+K_{n-1}r^{n-1}+K_{n-2}r^{n-2}+...+K_1r^1+K_0r^0
|
||||||
|
$$
|
||||||
|
感觉晕头转向?我们直接拿这个**二进制数**`010110`为例:
|
||||||
|
$$
|
||||||
|
010110 = 0\times2^5+1\times2^4+0\times2^3+1\times2^2+1\times2^1+0\times2^0
|
||||||
|
$$
|
||||||
|
于是它所对应的十进制数就是:$2^4+2^2+2^1=16+4+2=22$
|
||||||
|
|
||||||
|
怎么样,是不是很简单?
|
||||||
|
|
||||||
|
#### 十六进制数
|
||||||
|
|
||||||
|
你一定听说过这么个名词:**32位操作系统**和**64位操作系统**。
|
||||||
|
|
||||||
|
没错,**32位操作系统**代表着,对于`int`整形的变量,一共有32个**比特**用来存储这么一个数。
|
||||||
|
|
||||||
|
也就是说,二进制数的长度最长可以有32位。
|
||||||
|
|
||||||
|
好多!那么程序员在调试的时候总不能用手指头指着屏幕上的0和1一个一个数过去吧?
|
||||||
|
|
||||||
|
于是我们在此引入**十六进制数**。
|
||||||
|
|
||||||
|
我们规定,$A$代表$10$,$B$代表$11$,$C$代表$12$,以此类推,$F$代表$15$。
|
||||||
|
|
||||||
|
那么完整的16进制个位数便是:$123456789ABCDEF$
|
||||||
|
|
||||||
|
你惊人的注意力一定注意到了,这刚好对应**4位2进制数**!
|
||||||
|
|
||||||
|
于是,我们可以将一个很长的**二进制数**`01101011`聚合为两个**十六进制数**`6B`
|
||||||
|
|
||||||
|
### 2. 二进制数的编码表示
|
||||||
|
|
||||||
|
#### 机器数
|
||||||
|
|
||||||
|
现在想必你已经能够熟练的使用二进制数进行加减法了,那我们来想想这么一个情况。
|
||||||
|
|
||||||
|
假设一个计算机的寄存器中,只有8个位置,即只能存8个0或1,我们该如何存正数和负数呢?
|
||||||
|
|
||||||
|
很简单,拿出第一个位置,规定:
|
||||||
|
|
||||||
|
<b>第一个位置存0,代表这是一个正数;存1,代表这是一个负数。</b>
|
||||||
|
|
||||||
|
那么我们能表达的最小数字便是:`11111111`即$-127$
|
||||||
|
|
||||||
|
最大数是:`01111111`即`127`
|
||||||
|
|
||||||
|
但是接下来的问题头疼了,我们怎么对他们进行运算?
|
||||||
|
|
||||||
|
#### 原码vs补码
|
||||||
|
|
||||||
|
我们知道,为了尽可能简化CPU内部部件以实现资源的最大化利用,我们的CPU基础部件只能进行加法运算。
|
||||||
|
|
||||||
|
那么对于两个正数,我们很好计算,比如$1+1$:
|
||||||
|
$$
|
||||||
|
1+1=0000\ 0001+0000\ 0001 = 0000\ 0010
|
||||||
|
$$
|
||||||
|
我们尝试把它变大一点:
|
||||||
|
$$
|
||||||
|
127+3=0111\ 1111+0000\ 0011=1 \ 0000\ 0010
|
||||||
|
$$
|
||||||
|
多出来了一位!然而很可惜,我们计算机只能存<b>8个bit</b>,所以多出来的`1`我们只能忍痛丢掉,计算结果就是
|
||||||
|
$$
|
||||||
|
0000\ 0010 = 2
|
||||||
|
$$
|
||||||
|
这就是计算过程中发生了<b>溢出</b>。导致计算结果和实际结果不一致。
|
||||||
|
|
||||||
|
但是你又用惊人的注意力发现了:计算结果是2……这刚好就是……?!
|
||||||
|
$$
|
||||||
|
130\bmod 128=2
|
||||||
|
$$
|
||||||
|
没错,我们这样子计算,刚好就是在将计算结果对$128$进行<b>[模运算](https://zh.wikipedia.org/zh-cn/%E6%A8%A1%E7%AE%97%E6%95%B8)</b>!
|
||||||
|
|
||||||
|
那我们是不是可以巧妙地利用这一特性,通过加法来实现减法呢?!
|
||||||
|
|
||||||
|
比如说我们有两个数:$50$和$30$,我们需要计算$50-30$
|
||||||
|
|
||||||
|
既然在这个计算机中,我们计算的结果都是
|
||||||
|
$$
|
||||||
|
(A+B) \bmod 128
|
||||||
|
$$
|
||||||
|
那$50-30$就可以看作
|
||||||
|
$$
|
||||||
|
\begin{aligned}
|
||||||
|
[50+(-30)]\bmod 128
|
||||||
|
&=[50+(-30\bmod 128)]\bmod 128\\
|
||||||
|
&=[50+98]\bmod 128\\
|
||||||
|
&=148\bmod 128\\
|
||||||
|
&=20
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
哇!天才!<del>台下观众欣喜若狂!</del>
|
||||||
|
|
||||||
|
对于二进制数来说,我们怎么表达它呢?
|
||||||
|
|
||||||
|
我们在此引入**补码**的概念。
|
||||||
|
|
||||||
|
$-30$的二进制原码是:`1001 1110`
|
||||||
|
|
||||||
|
现在我们除了符号位以外,全部给他取反得到反码:`1110 0001`
|
||||||
|
|
||||||
|
再给它末位加上一,就可以得到**补码**:`1110 0010`。
|
||||||
|
|
||||||
|
对于正数,我们则规定**补码就是其本身**。
|
||||||
|
|
||||||
|
那么对于$50-30$,我们可以在运算时使用$[50]_{补}+[-30]_{补}$,对于这个计算就变成了
|
||||||
|
$$
|
||||||
|
0011 \ 0010 + 1110\ 0010 = 1\ 0001\ 0100
|
||||||
|
$$
|
||||||
|
丢弃溢出的最高位,我们得到`0001 0100`也就是$20$。
|
||||||
|
|
||||||
|
#### 补码的其他好处
|
||||||
|
|
||||||
|
如果使用原码,我们会发现一个很尴尬的情况:0有两种表示方法。
|
||||||
|
|
||||||
|
即`1000 0000`表示`-0`,`0000 0000`表示`+0`。
|
||||||
|
|
||||||
|
但在补码中,我们可以规定`1000 0000`表示$-128$,这样就比原码能多存一个数字!表示范围也扩展为$-128$~$127$。
|
||||||
|
|
||||||
|
#### 有符号数vs无符号数
|
||||||
|
|
||||||
|
以上的带符号的数我们称为**有符号数**。它们在计算机中以**补码**形式存储。
|
||||||
|
|
||||||
|
但是对于一些其他需求,我们不需要带符号的数,那么我们在计算机中就直接以**原码**的形式存储,并且**不带符号位**。
|
||||||
|
|
||||||
|
例如在一个8位操作系统中,你使用
|
||||||
|
|
||||||
|
```C
|
||||||
|
unsigned int i = 129;
|
||||||
|
```
|
||||||
|
|
||||||
|
那么它存的就直接是`1000 00001`
|
||||||
Loading…
Reference in a new issue