Compare commits

...

16 commits

Author SHA1 Message Date
github-actions[bot]
a743d9e2c8 Merge remote-tracking branch 'origin/master' into blog-content 2026-09-18 08:42:12 +00:00
ClovertaTheTrilobita
6ae02879f7
Merge pull request #44 from ClovertaTheTrilobita/dev
Some checks are pending
Sync and Deploy Astro Blog / sync-blog-content (push) Waiting to run
Sync and Deploy Astro Blog / build-and-deploy (push) Blocked by required conditions
Sync and Deploy Astro Blog / manual-deploy (push) Waiting to run
fix menu button
2026-09-18 16:42:03 +08:00
12539b16a8 fix menu button 2026-09-18 16:41:41 +08:00
github-actions[bot]
3af1161365 Merge remote-tracking branch 'origin/master' into blog-content 2026-09-18 08:31:02 +00:00
ClovertaTheTrilobita
c17d49de3f
Merge pull request #43 from ClovertaTheTrilobita/dev
fix latest comments
2026-09-18 16:30:53 +08:00
0cc02888f7 fix latest comments 2026-09-18 16:30:00 +08:00
github-actions[bot]
495caf993a Merge remote-tracking branch 'origin/master' into blog-content 2026-09-18 07:12:53 +00:00
ClovertaTheTrilobita
7aeb6a33da
Merge pull request #42 from ClovertaTheTrilobita/dev
update font size
2026-09-18 15:12:45 +08:00
881320f1e4 add en draft 2026-09-18 15:12:18 +08:00
767861bc13 update font size 2026-09-18 14:46:40 +08:00
be7ff22ed7 Finish content 2026-09-18 14:42:39 +08:00
984a2789cf Merge branch 'blog-content' of https://github.com/ClovertaTheTrilobita/SanYeCao-blog into blog-content 2026-09-18 00:16:05 +08:00
0f91434607 update draft 2026-09-18 00:14:24 +08:00
97bdca04f9 update draft 2026-09-17 22:24:14 +08:00
0b2c6902cc save draft 2026-09-17 09:44:45 +08:00
1dc17dfc8c added draft 2026-09-10 16:25:52 +08:00
6 changed files with 788 additions and 3 deletions

View file

@ -0,0 +1,388 @@
---
title: "How to Teach a Computer Arithmetic?? Addition and Subtraction"
pubDate: 2026-09-18
description: "Welcome to Cirno's Perfect Math Class. Let us start with... addition and subtraction!"
author: "Cloverta"
image:
url: "https://files.seeusercontent.com/2026/09/17/Dn5p/_20260917235541_13_363.webp"
alt: "ciruno"
tags: ["Study Notes", "Computer Organization"]
---
<p style="font-size: 0.85rem;"><i><sub>Content translated by <a href="https://chatgpt.com/">ChatGPT</a>.</sub></i></p>
What is 1+1?
This question has troubled people for many years.
Some say one plus one equals 1, because two pieces of maltose candy stuck together are still one piece of maltose candy.
Some say one plus one equals 2, because if you double-click `Calculator.exe`, enter 1+1, the output is 2.
Some say...
<br>
...I have run out of things to make up.
So, good morning, afternoon, and evening to all the kids and grown-ups out there. Today, we are going to teach a computer how to perform addition and subtraction.
It is that simple.
Huh? It is not actually simple?
## Zero: Let Us Start from Zero
Suppose your mom yells from the kitchen, “Kiddo, come eat! The food is getting coooold!” But you cannot rush to the dining table right away, because you are busy secretly using your mom's Alipay to buy *That Game 6*! The only thing within reach is the mysterious switch for the dining-room chandelier. How can you convey a message to your mom?
Easy! Just control whether the light is on! We treat a lit bulb as `True` (or, more simply, `1`) and an unlit bulb as `False` (or `0`). That way, you can transmit information efficiently!
You then resolutely switch off the dining-room light.
Bad ending: your mom knows nothing about the arcane mysteries of computer organization. From her point of view...
<i>This little brat is asking for a beating again. (grabs a clothes hanger)</i>
What now? The end of the world is at hand! But do not panic: your mom still has ten seconds before she bursts through the door, which is more than enough time for us to begin the next section.
## One: How Numbers Are Represented in a Computer
### 1. Binary
Now, imagine that you are holding an electrical wire. Yes, the very wire connected to the dining-room chandelier. Think back: how did you transmit a signal through the wire?
Like this: powered means `1`, and unpowered means `0`.
But that can represent only two states. The world is not simply black and white, so what if we need to represent four states?
Easy. Add another wire.
Now we can control the two wires to represent the four states `00`, `01`, `10`, and `11`. Let us number them!
`00` represents `0`; `01` represents `1`; `10` represents `01+01`, carrying one to the next place, so it is `2`; and `11` is `10+01`, so it is `3`.
That is right: this is how **binary** represents data.
In computers, we generally use binary for basic operations. The reason is actually quite obvious: if we used decimal, we would need to design ten different circuits for ten different states. Binary is much simpler—with a basic electrical wire, powered means 1 and unpowered means 0.
#### Converting Binary
Suppose you are given a random **binary number**, `010110`. How do we know what number it corresponds to?
Let us first look at a decimal number.
Take any decimal number, such as `21367`. It can be broken down intuitively as
$$
2\times10^4+1\times10^3+3\times10^2+6\times10^1+7\times10^0=21367
$$
Then, intuitively, for the <b>base-$r$</b> number `21367`, we can write
$$
2\times r^4+1\times r^3+3\times r^2+6\times r^1+7\times r^0=21367_r
$$
More generally, a <b>base-$r$ number</b> $K_nK_{n-1}K_{n-2}...K_1K_0$ can be expressed as
$$
K_nr^n+K_{n-1}r^{n-1}+K_{n-2}r^{n-2}+...+K_1r^1+K_0r^0
$$
Feeling dizzy? Let us use the **binary number** `010110` directly as an example:
$$
010110 = 0\times2^5+1\times2^4+0\times2^3+1\times2^2+1\times2^1+0\times2^0
$$
So its corresponding decimal value is $2^4+2^2+2^1=16+4+2=22$.
See? Is that not simple?
#### Hexadecimal Numbers
You must have heard the terms **32-bit operating system** and **64-bit operating system**.
That is right: a **32-bit operating system** means that an `int`-type variable has a total of 32 **bits** for storing a number.
In other words, an integer binary number can be up to 32 bits long.
That is a lot! Surely programmers debugging a program cannot point at the zeros and ones on the screen and count them one by one with their fingers?
And so we introduce **hexadecimal numbers**.
We define $A$ as $10$, $B$ as $11$, $C$ as $12$, and so on, up to $F$ as $15$.
Thus, the complete set of single hexadecimal digits is $0123456789ABCDEF$.
Your astonishing powers of observation must have noticed that this corresponds exactly to a **4-bit binary number**!
Therefore, we can group the long **binary number** `01101011` into two **hexadecimal digits**, `6B`.
### 2. Encoded Representations of Binary Numbers
#### Machine Numbers
By now, you must be able to perform addition and subtraction with binary numbers proficiently. So let us consider the following situation.
Suppose a computer register has only eight positions—that is, it can store only eight zeros or ones. How should we store positive and negative numbers?
Easy. Take the first position and define it as follows:
<b>If the first position stores 0, the number is positive; if it stores 1, the number is negative.</b>
The smallest number we can represent is therefore `11111111`, or $-127$.
The largest is `01111111`, or `127`.
But now we have a headache: how do we perform operations on them?
#### Sign-Magnitude vs. Two's Complement
We know that, to simplify the components inside the CPU as much as possible and maximize resource utilization, the basic components of our CPU can perform only addition.
For two positive numbers, calculation is easy. For example, $1+1$:
$$
1+1=0000\ 0001+0000\ 0001 = 0000\ 0010
$$
Let us try making the numbers a little larger:
$$
127+3=0111\ 1111+0000\ 0011= 1000\ 0010
$$
There is an extra bit! Unfortunately, our computer can store only <b>8 bits</b>, so although we put the extra `1` in the sign bit, that is incorrect. After removing the sign bit, the result is
$$
000\ 0010 = 2
$$
This is an <b>overflow</b> during computation, causing the computed result to differ from the actual result.
But your astonishing powers of observation have spotted something else: the result is 2... and that happens to be...?!
$$
130\bmod 128=2
$$
That is right. Calculating this way is exactly equivalent to taking the result <b>[modulo](https://zh.wikipedia.org/zh-cn/%E6%A8%A1%E7%AE%97%E6%95%B8)</b> $256$. Could we cleverly exploit this property to implement subtraction through addition?
For example, suppose we have two numbers, $50$ and $30$, and need to calculate $50-30$.
Since, on this computer, all of our results are
$$
(A+B) \bmod 128
$$
we can regard $50-30$ as
$$
\begin{aligned}
[50+(-30)]\bmod 128
&=[50+(-30\bmod 128)]\bmod 128\\
&=[50+98]\bmod 128\\
&=148\bmod 128\\
&=20
\end{aligned}
$$
Wow! Genius! <del>The audience erupts in delight!</del>
How do we represent this with binary numbers?
Here, we introduce the concept of **two's complement**.
The sign-magnitude binary representation of $-30$ is `1001 1110`.
Now invert every bit except the sign bit to obtain the one's complement: `1110 0001`.
Then add one to the last bit to obtain the **two's complement** representation: `1110 0010`.
For positive numbers, we define their **two's complement representation as the number itself**.
Thus, for $50-30$, we can use $[50]_{\text{two's complement}}+[-30]_{\text{two's complement}}$ during the operation, turning the calculation into
$$
0011 \ 0010 + 1110\ 0010 = 1\ 0001\ 0100
$$
Discarding the overflowing highest bit gives us `0001 0100`, which is $20$.
#### Other Benefits of Two's Complement
If we use sign-magnitude representation, we encounter an awkward situation: zero has two representations.
That is, `1000 0000` represents `-0`, while `0000 0000` represents `+0`.
With two's complement, however, we can define `1000 0000` as $-128$. This lets us store one more number than sign-magnitude representation can! The range also expands to $-128$–$127$.
#### Signed vs. Unsigned Numbers
The signed numbers above are called **signed numbers**. They are stored in computers in **two's complement** form.
For some other needs, however, we do not require signed numbers. In that case, we store them directly in the computer in **sign-magnitude** form, with **no sign bit**.
For example, on an 8-bit operating system, if you write
```c
unsigned int i = 129;
```
then it directly stores `1000 0001`. Because no sign bit needs to be stored, the storage range of an unsigned number on an 8-bit machine is 0–255.
## Two: Addition and Subtraction Circuits
In a computer, a **traditional** **arithmetic unit** consists of an **arithmetic logic unit (ALU)**, a **shifter**, a **program status word (PSW) register**, a **general-purpose register set**, and other components.
But let us not worry about all that yet. First, let us look at the ALU. The core component of an ALU is the **adder**.
### 1. One-Bit Full Adder
Let us recall how we calculate by hand.
When calculating $1011101+0101100$, we use column addition:
$$
\begin{array}{r}
1011101 \\
+\,0101100 \\
\hline
10001001
\end{array}
$$
That is, we add each pair of digits separately; if the sum is at least 2, we carry 1 into the next higher place.
A computer performs addition using the same idea. Let us first imagine the simplest addition circuit: a **one-bit full adder**.
When performing addition for a single position, we need to process **three inputs**: addend $A_i$, addend $B_i$, and the carry $C_{i-1}$ from the lower position. It also produces **two outputs**: the current-position sum $S_i$ and the carry $C_i$ to the higher position.
Let us first analyze how to calculate $S_i$:
- When $A_i$ and $B_i$ are the same, the current-position digit of their sum must be `0`. After all, $0+0=0$ and $1+1=10$.
- When $A_i$ and $B_i$ differ, the current-position digit of their sum must be `1`.
Following this idea, the binary calculation $A_i+B_i$ can be represented by the XOR logic circuit, namely $A_i \oplus B_i$.
Now let us put the carry $C_i$ into this addition expression in the same way: if $(A_i \oplus B_i)$ and $C_i$ are the same, the result is `0`; if $(A_i \oplus B_i)$ and $C_i$ differ, the result is `1`. In other words, the calculation can be represented as $(A_i \oplus B_i) \oplus C_i$. Therefore:
$$
S_i = A_i \oplus B_i \oplus C_{i-1}
$$
Next, let us look at the carry $C_i$:
- When both $A_i$ and $B_i$ are `1`, a carry is produced. After all, $1+1=10$.
- When only one of $A_i$ and $B_i$ is `1`:
- If the carry $C_{i-1}$ from the previous position's operation is `1`, a carry is produced.
- If $C_{i-1}$ is `0`, then $1+0=1$, so there is certainly no carry.
In terms of the result: $A_i$ is `1` **and** $B_i$ is `1`; **or** $A_i$ and $B_i$ **differ** **and** $C_i$ is `1`.
Written as a logical expression, this is
$$
C_i=A_iB_i+(A_i \oplus B)C_{i-1}
$$
The logical structure of a one-bit full adder is therefore shown below.
<img src="https://files.seeusercontent.com/2026/09/17/s8Ns/eeb551b.png" alt="One-bit full adder.png" title="One-bit full adder" style="display: block; margin: 0px auto; zoom: 50%;">
If we encapsulate it and expose only its inputs and outputs, its logic symbol is:
<img src="https://files.seeusercontent.com/2026/09/17/Ut1i/drawio.png" alt="One-bit full adder.png" title="One-bit full adder" style="display: block; margin: 0px auto; zoom: 67%;">
### 2. Ripple-Carry Adder
Cascading $n$ full adders forms an $n$-bit <b>serial-carry adder</b> (also called a ripple-carry adder), as shown below.
<img src="https://files.seeusercontent.com/2026/09/17/V1ov/drawio.png" alt="One-bit full adder.png" title="One-bit full adder" style="display: block; margin: 0px auto; zoom: 67%;">
Chaining them together this way makes it easy to add two binary numbers.
Encapsulating them as a single unit gives us a traditional **adder**.
### 3. Adder with Flags
For an $n$-bit adder, besides obtaining the result, we often need to know whether overflow occurred during the calculation, whether the result is positive or negative, whether the result is zero, and so on.
We require the adder to generate the following flags:
- $OF$: **overflow flag**. `1` indicates overflow, while `0` indicates no overflow. $OF=C_n \oplus C_{n-1}$
- $SF$: **sign flag**. It equals the most significant bit of the result; `1` indicates negative, while `0` indicates positive. $SF=S_{n-1}$
- $ZF$: **zero flag**. `1` indicates that the result of the addition or subtraction is $0$. It is set to `1` when every bit is `0`.
- $CF$: <b>carry/borrow flag</b>. It is used to determine whether overflow occurred in an unsigned addition or subtraction. `1` indicates overflow, while `0` indicates no overflow.
The adder's symbol can be represented as follows:
<img src="https://files.seeusercontent.com/2026/09/17/tE8g/_20260918000903_15_363.webp" alt="One-bit full adder.png" title="One-bit full adder" style="display: block; margin: 0px auto; zoom: 18%;">
#### Methods for Detecting Overflow
We know that addition and subtraction using two's complement can overflow only when **adding numbers with the same sign** or **subtracting numbers with different signs**. This is easy to understand intuitively, because when two numbers with the same sign are subtracted, the absolute value of the result must be smaller than the absolute value of either the subtrahend or the minuend.
On an 8-bit machine, when a result exceeds 127, it wraps around to -128. For example, $127+3=-126$. Based on this, we can devise a very intuitive detection method: **when two numbers have the same sign, overflow has occurred if the sign of the result differs from theirs**.
##### 1) **Using One Sign Bit**
Suppose the sign bits of the two numbers taking part in the operation are $A_i$ and $B_i$, respectively, and the sign bit of the result is $S_i$. We obtain the overflow logic expression:
$$
V=A_iB_i\overline{S_i}+\overline{A_i}\overline{B_i}S_i
$$
##### 2) Using **One Sign Bit** Together with the **Carry**
- If both numbers are positive, their sign bits are both `0`, so they certainly do not produce a carry into the next position.
- If the second-highest position does not carry into it, the sign has not changed, and the sign bit remains `0`.
- If the second-highest position does carry into it, the sign bit becomes `1`; the sign has changed, so overflow has occurred.
- The same principle applies when both numbers are negative. Their sign bits are both `1`, so they necessarily produce a carry into the next position, while the current-position digit after addition is `0`.
- If the second-highest position carries `1` into it, the sign bit remains `1`, and no overflow has occurred.
- If the second-highest position does not carry into it, the sign bit is `0`; the sign has changed, so overflow has occurred.
To summarize, **overflow occurs if the carry from the highest position differs from the carry from the second-highest position**.
In other words, suppose the carry produced by the sign bit after the calculation is $C_n$, and the carry produced by the second-highest position (the highest value bit) is $C_{n-1}$. If $C_n$ differs from $C_{n-1}$, overflow is indicated.
$$
V=C_{i} \oplus C_{i-1}
$$
## Three: Addition and Subtraction Circuit
Finally! Here comes the big one! But before that, we need to cover a small interlude.
### 1. MUX Multiplexer
In an addition and subtraction circuit, we need a **2-to-1 multiplexer (MUX)** to control whether addition or subtraction is performed.
The idea is simple: for subtraction, take the additive inverse of the subtrahend. That is,
$$
X-Y \rightarrow X+(-Y)
$$
We feed both $Y$ and $\overline{Y}$ into the MUX and give it a signal, Sub.
- If Sub is `0`, addition is being performed, so the MUX selects $Y$ as its output.
- If Sub is `1`, subtraction is being performed, so the MUX selects $\overline{Y}$ as its output.
> $\overline{Y}$ is the <b>inversion</b> of $Y$. If $Y=1010$, then $\overline{Y}=0101$.
<img src="https://files.seeusercontent.com/2026/09/18/mTy9/drawio.png" alt="One-bit full adder.png" title="One-bit full adder" style="display: block; margin: 0px auto; zoom: 67%;">
### 2. Arithmetic Circuit
Connecting the MUX to one end of the adder gives us the following circuit:
<img src="https://files.seeusercontent.com/2026/09/18/Yw6j/drawio.png" alt="One-bit full adder.png" title="One-bit full adder" style="display: block; margin: 0px auto; zoom: 67%;">
In a computer, addition and subtraction of both signed and unsigned numbers are implemented using this same circuit. Its inputs include two $n$-bit operands, $X$ and $Y$, as well as a control signal, $Sub$.
The control signal $Sub$ not only determines which data path enters the adder; when subtraction is performed (when $Sub$ is `1`), it also serves as the carry input to the lowest bit.
Now everything is ready. Let us analyze, step by step, how addition and subtraction work.
#### Addition
- $X$ is fed directly into the adder.
- $Sub$ is `0`, so the MUX selects $Y$ and feeds it into the adder.
- The adder directly computes $X+Y+C_{in}$, outputs the $n$-bit result $F$ and the carry output $C_{out}$, and generates the status flags.
It is worth noting that if $X$ and $Y$ are unsigned numbers, then the result is $F=(X+Y)\mod2^n$. If $X+Y\ge2^n$, a carry is produced, so $C_{out}=1$, indicating that unsigned overflow has occurred.
#### Subtraction
- $X$ is fed directly into the adder.
- $Sub$ is `1`, so the MUX selects $\overline{Y}$ and feeds it into the adder.
- The adder computes $X+\overline{Y}+C_{in}$, namely $X+\overline{Y}+1$.
If we are calculating the subtraction of signed numbers, then inverting $[Y]_{\text{two's complement}}$ and adding 1 gives exactly $[-Y]_{\text{two's complement}}$, so the operation is equivalent to $X+(-Y)$.
In addition, when calculating unsigned subtraction, we define the **overflow flag OF as the inverse of $C_{out}$**. That is, $OF=\overline{C_{out}}$. This is exactly the opposite of unsigned addition.
This is because the operation is equivalent to $X-Y+2^n$. Therefore:
- When $X\ge Y$, $X-Y+2^n \ge 2^n$, so there is a carry.
- When $X<Y$, $X-Y+2^n < 2^n$, so there is no carry and $C_{out}=0$. At this point, $OF=\overline{C_{out}}=1$, indicating overflow.
## Epilogue
Your mom bursts through the door and glares furiously at your computer screen—but by now, you have [Cloverta's Blog](https://blog.cloverta.top) open!
It turns out that you were learning binary addition and subtraction. She nods in satisfaction. This person's blog is pretty good, so remember to add its RSS feed to your subscriptions.
<img src="https://files.seeusercontent.com/2026/09/18/H6qn/100px-THBWiki-LOGO.gif" alt="One-bit full adder.png" title="One-bit full adder" style="display: block; margin: 0px auto; zoom: 67%;">

View file

@ -0,0 +1,385 @@
---
title: "如何教会计算机算术??加减篇"
pubDate: 2026-09-18
description: '欢迎来到琪露诺的奇妙数学教室,让我们从……加减法开始吧!'
author: "三叶"
image:
url: "https://files.seeusercontent.com/2026/09/17/Dn5p/_20260917235541_13_363.webp"
alt: "ciruno"
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_r
$$
更通用地,对于<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进制个位数便是:$0123456789ABCDEF$
你惊人的注意力一定注意到了,这刚好对应**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= 1000\ 0010
$$
多出来了一位!然而很可惜,我们计算机只能存<b>8个bit</b>,所以多出来的`1`我们虽然放在符号位,但这是不对的,去除符号位的计算结果就是
$$
000\ 0010 = 2
$$
这就是计算过程中发生了<b>溢出</b>。导致计算结果和实际结果不一致。
但是你又用惊人的注意力发现了:计算结果是2……这刚好就是……?!
$$
130\bmod 128=2
$$
没错,我们这样子计算,刚好就是在将计算结果对$256$进行<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 0001`。由于不需要存符号位,无符号数在8bit机器上的存储范围就是0~255。
## 二、加减法的运算电路
在计算机中,**传统**的**运算器**由**算术逻辑单元ALU**、**移位器**、**状态寄存器PSW**和**通用寄存器组**等组成。
但是我们先别管那么多,首先我们来看ALU。ALU的核心部件是**加法器**。
### 1. 一位全加器
我们回忆一下手写运算。
当我们计算$1011101+0101100$时,用竖式来计算就是
$$
\begin{array}{r}
1011101 \\
+\,0101100 \\
\hline
10001001
\end{array}
$$
也就是,对每一位数单独进行加法运算,如果加和结果≧2,则向高位进1。
同样地,计算机进行加法运算也是这种思路。我们先试想一个最简单的加法电路:**一位全加器**。
在进行一个单位的加法时,我们需要处理**三个输入**:加数$A_i$、加数$B_i$、来自低位的进位$C_{i-1}$。它也会产生**两个输出**:加和结果的本位$S_i$和想高位的进位$C_i$。
我们先来分析一下如何计算$S_i$:
- 当$A_i$与$B_i$相同,它们俩的加和结果的本位肯定是`0`。毕竟$0+0=0$,$1+1=10$。
- $A_i$与$B_i$不同是,它们的加和结果肯定是`1`。
根据这一思路,计算二进制$A_i+B_i$可以用逻辑电路XOR表示,即$A_i \oplus B_i$。
现在我们把进位$C_i$用同样的思路放进这个加法式中:若$(A_i \oplus B_i)$和$C_i$相同则为`0`,$(A_i \oplus B_i)$和$C_i$不同则为`1`。即计算可以用$(A_i \oplus B_i) \oplus C_i$来表示,那么就有:
$$
S_i = A_i \oplus B_i \oplus C_{i-1}
$$
我们接下来看进位$C_i$:
- 当$A_i$和$B_i$均为`1`,则产生进位。毕竟$1+1=10$。
- 当$A_i$和$B_i$只有一个为`1`时:
- 若上一位运算的进位$C_{i-1}$是`1`,则产生进位。
- 如果$C_{i-1}$是`0`,那$1+0=1$肯定不产生进位。
从结果上来讲,就是:$A_i$为`1`**且**$B_i$为`1`,**或**,$A_i$和$B_i$**相异** **且** $C_i$为`1`。
整理成逻辑表达式就是:
$$
C_i=A_iB_i+(A_i \oplus B)C_{i-1}
$$
那么一位全加器的逻辑结构就如下图所示。
<img src="https://files.seeusercontent.com/2026/09/17/s8Ns/eeb551b.png" alt="一位全加器.png" title="一位全加器" style="display: block; margin: 0px auto; zoom: 50%;">
我们把它封装起来,仅对外保留输入输出,那它的逻辑符号表示为:
<img src="https://files.seeusercontent.com/2026/09/17/Ut1i/drawio.png" alt="一位全加器.png" title="一位全加器" style="display: block; margin: 0px auto; zoom: 67%;">
### 2. 串行进位加法器
将$n$个全加器级联就可以构成$n$位<b>串行进位加法器</b>(又称行波进位加法器),如下图所示
<img src="https://files.seeusercontent.com/2026/09/17/V1ov/drawio.png" alt="一位全加器.png" title="一位全加器" style="display: block; margin: 0px auto; zoom: 67%;">
将它们这样子串在一起,就可以轻松实现两个二进制数的加法。
我们把它封装成一个整体,就得到了传统的**加法器**。
### 3. 带标志加法器
对于$n$位加法器来说,除了得到运算结果外,我们经常需要知道计算过程中是否发生了溢出、结果正负性、结果是否为零等等信息。
我们要求加法器能生成以下标志位:
- $OF$:**溢出标志**,为`1`表示溢出,`0`表示未溢出。$OF=C_n \oplus C_{n-1}$
- $SF$:**符号标志**,等于结果的最高有效位,`1`表示负,`0`表示正。$SF=S_{n-1}$
- $ZF$:**零标志**,`1`表示加减运算的结果为$0$。在所有位均为`0`时设置为`1`。
- $CF$:<b>进位/借位标志</b>,用于判断无符号数的加减运算是否发生溢出。`1`表示溢出,`0`表示未溢出。
加法器的符号就可以表示为:
<img src="https://files.seeusercontent.com/2026/09/17/tE8g/_20260918000903_15_363.webp" alt="一位全加器.png" title="一位全加器" style="display: block; margin: 0px auto; zoom: 18%;">
#### 溢出的判别方法
我们知道,补码的加减法运算仅在**同号相加**或**异号相减**的情况下可能溢出。这在直观上很好理解,因为两个同号数相减时,结果的绝对值肯定会小于减数或被减数的绝对值。
在8bit机器中,当运算结果超出127时,它会回到-128。例如$127+3=-126$。我们依此可以设计出一个非常直观的判断方法:**当两个数的符号相同时,若计算结果的符号和它们不同,则发生了溢出**。
##### 1)**采用一位符号位**
设参与运算的两个数的符号位分别为$A_i$和$B_i$,运算结果的符号位是$S_i$,可得溢出逻辑表达式:
$$
V=A_iB_i\overline{S_i}+\overline{A_i}\overline{B_i}S_i
$$
##### 2)采用**一位符号位**并结合**进位**
- 如果两个数是正数,那它的符号位都是`0`,肯定不会向后进位。
- 此时如果次高位未向它进位,说明符号没有改变,符号位仍然是`0`。
- 此时次高位向它进了一位,那符号位就变成`1`了,符号改变,发生溢出。
- 两数为负数时同理,它们符号位都是`1`,必会向后进一位,且相加后本位是`0`。
- 如果次高位向它进`1`,则符号位还是`1`,未溢出。
- 若次高位未向它进位,则符号位`0`,符号改变,发生溢出。
总结一下,**如果最高位和次高位发生的进位不同,则溢出**。
换言之,假设计算后符号位产生的进位是$C_n$,次高位(数值的最高位)产生的进位是$C_{n-1}$,若$C_n$与$C_{n-1}$不同,则表示溢出。
$$
V=C_{i} \oplus C_{i-1}
$$
## 三、加减运算电路
Finally! 大的要来了!但是在这之前我们要了解一个小插曲。
### 1. MUX多路选择器
在加减法电路中,我们需要一个**二选一多路选择器MUX**,用它来控制进行加法还是减法。
思路很简单,如果是减法,则给减数取相反数。也就是
$$
X-Y \rightarrow X+(-Y)
$$
我们同时给MUX输入$Y$和$\overline{Y}$,并给其一个信号Sub
- 如果Sub为`0`,则说明进行加法,MUX选择$Y$输出
- 如果Sub是`1`,进行减法,MUX选择$\overline{Y}$输出
> $\overline{Y}$是$Y$的<b>反相</b>。如果$Y=1010$,则$\overline{Y}=0101$。
<img src="https://files.seeusercontent.com/2026/09/18/mTy9/drawio.png" alt="一位全加器.png" title="一位全加器" style="display: block; margin: 0px auto; zoom: 67%;">
### 2. 运算电路
我们把MUX接到加法器的一端,就有了如下电路:
<img src="https://files.seeusercontent.com/2026/09/18/Yw6j/drawio.png" alt="一位全加器.png" title="一位全加器" style="display: block; margin: 0px auto; zoom: 67%;">
在计算机中,有符号数和无符号数的加减运算,均采用这样的同一套电路实现。它的输入端包括连个$n$位操作数$X$和$Y$,还有一个控制信号$Sub$。
控制信号$Sub$不仅决定选择哪一路数据进入加法器,在执行减法时($Sub$为`1`)作为最低位的进位输入。
现在大功告成了,我们来一步一步解析加减法运算时的工作原理。
#### 加法运算
- $X$直接被输入到加法器中
- $Sub$为`0`,所以MUX选择$Y$输入到加法器中
- 加法器直接运算$X+Y+C_{in}$,并输出$n$位结果$F$和进位输出$C_{out}$,并生成状态标志位
值得注意的是,如果$X$和$Y$是无符号数,那么结果$F=(X+Y)\mod2^n$,如果$X+Y\ge2^n$,就会产生进位$C_{out}=1$,就表示发生无符号溢出。
#### 减法运算
- $X$直接被输入到加法器中
- $Sub$为`1`,所以MUX选择$\overline{Y}$输入到加法器
- 加法器运算$X+\overline{Y}+C_{in}$,也就是$X+\overline{Y}+1$
如果此时我们在计算有符号数的减法,那么$[Y]_补$取反再+1刚好就是$[-Y]_补$,所做的运算就等同于$X+(-Y)$。
另外,如果计算的是无符号数减法,我们规定**溢出标志OF是$C_{out}$取反**。即$OF=\overline{C_{out}}$。这一点和无符号数加法正好相反。
因为运算等价于$X-Y+2^n$,那么
- $X\ge Y$时,$X-Y+2^n \ge 2^n$,有进位。
- $X< Y$时,$X-Y+2^n < 2^n$,无进位$C_{out}=0$,此时$OF=\overline{C_{out}}=1$,表示溢出。
## 尾声
老妈推门而入,怒目圆瞪地盯着你的电脑屏幕,但此时你打开的是[三叶的博客](https://blog.cloverta.top)!
原来你在学习二进制加减法,她满意地点点头,这个人博客写的还可以的,你记得把他的RSS添加到订阅里。
<img src="https://files.seeusercontent.com/2026/09/18/H6qn/100px-THBWiki-LOGO.gif" alt="一位全加器.png" title="一位全加器" style="display: block; margin: 0px auto; zoom: 67%;">

View file

@ -2,7 +2,7 @@
const { lang = "zh" } = Astro.props;
const host = import.meta.env.PUBLIC_REMARK42_HOST;
const siteId = import.meta.env.PUBLIC_REMARK42_SITE_ID;
const max = 5;
const max = 7;
const heading = lang === "zh" ? "最新评论" : "Latest comments";
---

View file

@ -202,7 +202,7 @@ const tocHeadings = headings.filter((h) => h.depth === 2 || h.depth === 3);
position: fixed;
top: 0.75rem;
right: 0.8rem;
z-index: 30;
z-index: 40;
max-width: min(78vw, 22rem);
padding: 0.6rem 0.75rem;
border: 1.5px solid var(--deep-blue);
@ -252,7 +252,7 @@ const tocHeadings = headings.filter((h) => h.depth === 2 || h.depth === 3);
position: fixed;
top: calc(env(safe-area-inset-top) + 4.2rem);
right: 0.8rem;
z-index: 29;
z-index: 39;
width: min(88vw, 24rem);
max-height: min(65vh, 32rem);
overflow-x: hidden;
@ -299,6 +299,10 @@ const tocHeadings = headings.filter((h) => h.depth === 2 || h.depth === 3);
if (btn.dataset.bound === "true") return;
btn.dataset.bound = "true";
// Keep the fixed controls outside the animated article container so
// the header and its divider cannot create a higher paint layer.
document.body.append(btn, panel);
const defaultText =
preview.getAttribute("data-default-text") || "本文目录";

View file

@ -318,6 +318,7 @@ const publishedDate = new Intl.DateTimeFormat(
"Noto Sans CJK SC",
"Source Han Sans SC",
sans-serif;
font-size: 1.07rem;
font-weight: 400;
line-height: 1.85;
overflow-wrap: break-word;

View file

@ -151,11 +151,18 @@
.comment-source-context {
display: flex;
align-items: baseline;
flex: 1 1 auto;
gap: 0.4rem;
min-width: 0;
}
.comment-source-context > span {
flex: 0 0 auto;
white-space: nowrap;
}
.comment-title-link {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
color: #6f8090;