diff --git a/translated/tech/20221026.0 ⭐️⭐️⭐️ Doing 64-bit math on a 16-bit system.md b/translated/tech/20221026.0 ⭐️⭐️⭐️ Doing 64-bit math on a 16-bit system.md
index da47247bb9..d5aa943880 100644
--- a/translated/tech/20221026.0 ⭐️⭐️⭐️ Doing 64-bit math on a 16-bit system.md
+++ b/translated/tech/20221026.0 ⭐️⭐️⭐️ Doing 64-bit math on a 16-bit system.md
@@ -2,83 +2,84 @@
[#]: via: "https://opensource.com/article/22/10/64-bit-math"
[#]: author: "Jerome Shidel https://opensource.com/users/shidel"
[#]: collector: "lkxed"
-[#]: translator: "yzuowei "
+[#]: translator: "yzuowei"
[#]: reviewer: " "
[#]: publisher: " "
[#]: url: " "
-在16位系统上做64位数学
+如何在 16 位系统上进行 64 位数学运算
======
-只需要一点点汇编的基础理解,这些函数就能适应体任意大小的整型数学运算。
+![][0]
-几年前,我为 FreeDOS 写了一个命令行数学程序叫做 VMATH。它可以在很小的无符号整型上执行十分简单的数学运算。出于近来对 FreeDOS 社区里基本数学的兴趣,我改进了 VMATH 使其可以为有符号64位整型提供基本的数学支持。
+> 只要对汇编有一点基本的了解,这些函数就能扩展到任意位长的整型数学运算。
-仅使用兼容16位 8086 的汇编来操控大型数字的过程并不直接。我希望能够分享一些在 VMATH 中用到的技术的例子。其中一些方法掌握起来还挺容易。同时,也有着别的看起来有点奇怪的方法。你甚至可能学到一种全新的进行基本数学运算的方式。
+几年前,我为 FreeDOS 写了一个叫做 VMATH 的命令行数学程序。它只能在很小的无符号整型上执行十分简单的数学运算。随着近来 FreeDOS 社区里对基础数学的兴趣,我改进了 VMATH 使其可以为有符号 64 位整型提供基本的数学支持。
-接下来要讲的加,减,乘,除会用到的技术将不局限于将不局限于64位整型。只需要一点点汇编的基础理解,这些函数就能适应任意大小的整型数学运算。
+仅使用 16 位 8086 兼容的汇编指令来操控大型数字的过程并不简单。我希望能够分享一些在 VMATH 中用到的技术例子。其中一些方法掌握起来相当容易。而另外一些方法则看起来有点奇怪。你甚至可能学到一种进行基本数学运算的全新方式。
-在深挖这些数学函数前,我想要覆盖一些计算机看数字的基础视角。
+接下来要讲的加、减、乘、除会用到的技术将不局限于并不局限于 64 位整型。只要对汇编有一点基本的了解,这些函数就能扩展到任意位长的整型数学运算。
+
+在深入研究这些数学函数前,我想先从计算机的角度介绍一下数字的一些基本知识。
### 计算机是如何读取数字的
-一个兼容 Intel 的 CPU 以字节 (byte) 的形式贮存数字,储存顺序为从最低有效字节到最高有效字节。每个字节由8个二进位组成,两个字节组成一个字 (word)。
+一个英特尔兼容的 CPU 以字节的形式贮存数字,储存顺序为从最低有效字节到最高有效字节。每个字节由 8 个二进位组成,两个字节组成一个字。
-一个储存在内存里的64位整型占用了8个字节 (或4个字)。例如,数字74565(十六进制表示为0x12345)的值长得是这个样子的:
+一个储存在内存里的 64 位整型占用了 8 个字节(即 4 个字)。例如,数字 `74565`(十六进制表示为 `0x12345`)的值长得是这个样子的:
```
-as bytes: db 0x45, 0x23, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
-as words: dw 0x2345, 0x0001, 0x0000, 0x0000
+用字节表示:db 0x45, 0x23, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+用字表示:dw 0x2345, 0x0001, 0x0000, 0x0000
```
-当读取或写入数据到内存时,CPU 会以正确的顺序处理这些字节。对于一个比 8086 更现代的处理器而言,数据组可以再大些,比如一个四字组就可以表达整个64为整型为 **0x0000000000012345**。
+当读取或写入数据到内存时,CPU 会以正确的顺序处理这些字节。对于比 8086 更现代的处理器而言,数据分组可以再大些,比如一个四字组就可以表达整个 64 位整型 `0x0000000000012345`。
-8086 CPU 不能理解这么大的数字。当为 FreeDOS 编程时,你想要写的是一个能在任意电脑上跑的程序,甚至是早期的 IBM PC 5150。你想要使用能够适应任意大小整型的技术。我们并不关心现代 CPU 的能力。
+8086 CPU 不能理解这么大的数字。当为 FreeDOS 编程时,你想要写的是一个能在任意电脑上跑的程序,甚至是原始的 IBM PC 5150。你想要使用能够扩展到任意大小整型的技术。我们其实并不关心更现代 CPU 的能力。
为了能做整型运算,我们的数据需要表达两种不同类型的数字。
-第一种是无符号整型,其使用了所有字位来表达一个正数。无符号整型的值域为从 **0** 到 **(2 ^ (字位数量) - 1)**。例如,8位数可以是 **0** 到 **255** 之间的任意值,而16位数则在 **0** 到
-**65535** 之间,以此类推。
+第一种是无符号整型,其使用了所有的位来表达一个正数。无符号整型的值域为从 `0` 到 $2^{位长} - 1$。例如,8 位数可以是 `0` 到 `255` 之间的任意值,而 16 位数则在 `0` 到
+`65535` 之间,以此类推。
-有符号整型也很类似。不同之处在于数字的最显著位代表了这个数是一个整数 (**0**) 还是一个负数 (**1**)。有符号整型的值域前半部分位正数,正数值域是从 **0** 到 **(2 ^ (字位数量 - 1) - 1)**。整型值域的后半部分为负数,负数值域则从 **(0-(2 ^ (字位数量 - 1)))** 到 **-1**。
+有符号整型也很类似。不同之处在于数字的最高位代表了这个数是一个正数(`0`)还是一个负数 (`1`)。有符号整型的值域前半部分为正数,正数值域是从 `0` 到 $2^{(字长 - 1)} - 1$。整型值域的后半部分为负数,负数值域则从 $0 - (2^{位长 - 1})$ 到 `-1`。
-比如说,一个8位数代表着 **0** 到 **127** 之间的任意正数,以及 **-128** 到 **-1** 之间的任意负数。为了能更好的理解这一点,想象 **字节** 为一列数组 **[0...127,-128...-1]**。因为 **-128** 在数组内紧跟着 **127**,**127** 加 **1** 等于 **-128**。当然这可能看起来有点奇怪甚至反常,但这其实让这个层级的基本数学运算变简单了。
-
-为了能够对大型整型进行简单的加,减,乘,除,你应该摸索一些简单的公式来计算一个数的绝对值或负值。你在做有符号整型运算的时候会用上它们的。
+比如说,一个 8 位数代表着 `0` 到 `127` 之间的任意正数,以及 `-128` 到 `-1` 之间的任意负数。为了能更好的理解这一点,想象 **字节** 为一列数组 `[0...127,-128...-1]`。因为 `-128` 在数组内紧跟着 `127`,`127` 加 `1` 等于 `-128`。当然这可能看起来有点奇怪甚至反常,但这其实让这个层级的基本数学运算变简单了。
+为了能够对大型整型进行简单的加、减、乘、除,你应该摸索一些简单的公式来计算一个数的绝对值或负值。你在做有符号整型运算的时候会用上它们的。
### 绝对值与负值
-计算一个有符号整型的绝对值并没有它看起来的那么糟糕。由于无符号和有符号数字在内存里的储存形式,我们其实有一个简单的方案。你只需要翻转一个负数的所有字位,得出的结果再加 **1**。
+计算一个有符号整型的绝对值并没有它看起来的那么糟糕。由于无符号和有符号数字在内存里的储存形式,我们其实有一个简单的方案。你只需要翻转一个负数的所有字位,得出的结果再加 `1`。
-如果你从没接触过二进制的话这可能听上去有点奇怪,但这就是这么工作的。让我们来举一个例子,取一个负数的8位表达,比如说 **-5**。因为 **-5** 靠近 **[0...127,-128...-1]** 字节组末端,它的十六进制值为 **0xfb**,二进制值为 **11111011**。如果你翻转了所有字位,你会得到 **0x04** 或二进制值 **00000100**。结果加 **1** 你就得到了你的答案。你刚刚把 **-5** 的值变成了 **+5**。
+如果你从没接触过二进制的话,这可能听上去有点奇怪,但这就是这么工作的。让我们来举一个例子,取一个负数的 8 位表达,比如说 `-5`。因为 `-5` 靠近 `[0...127,-128...-1]` 字节组末端,它的十六进制值为 `0xfb`,二进制值为 `11111011`。如果你翻转了所有字位,你会得到 `0x04` 或二进制值 `00000100`。结果加 `1` 你就得到了你的答案:你刚刚把 `-5` 的值变成了 `+5`。
-你可以用汇编写下这个程序用以返回任意64位数字的绝对值:
+你可以用汇编写下这个程序用以返回任意 64 位数字的绝对值:
```
; 语法,NASM for DOS
proc_ABS:
- ; 启动时,SI寄存器会指向数据段 (DS) 内的内存位置,那里存放着程序内包含着
- ; 会被转正的64位数。
- ; 结束时,如果结果数字不能被转正,Carry Flag (CF) 会被设置。这种情况只
- ; 有在遇到最大负值时会发生。其余情况,CF 不会被设置。
+ ; 启动时,SI 寄存器会指向数据段(DS)内的内存位置,那里存放着程序内包含着
+ ; 会被转为正数的 64 位数。
+ ; 结束时,如果结果数字不能被转正,CF 寄存器会被设置。这种情况只
+ ; 有在遇到最大负值时会发生。其余情况,CF 不会被设置。
; 检查最高字节的最高位
test [si+7], byte 0x80
- ; 如不为1,值为正值
+ ; 如不为 1,值为正值
jz .done_ABS
- ; 翻转字的所有字位 #4
- not word [si+6]
+ ; 翻转所有位
+ not word [si+6] ; 字 #4
not word [si+4] ; 字 #3
not word [si+2] ; 字 #2
- not word [si] ; 字 #1
- ; 字#1 加一
+ not word [si] ; 字 #1
+ ; 字 #1 加 1
inc word [si]
- ; 如结果不为0,结束
+ ; 如结果不为 0,结束
jnz .done_ABS
- ; 字#2 加一
+ ; 字 #2 加 1
inc word [si+2]
- ; 如结果为0,进位下一个字
+ ; 如结果为 0,进位下一个字
jnz .done_ABS
inc word [si+4]
jnz .done_ABS
@@ -86,42 +87,42 @@ proc_ABS:
inc word [si+6]
; 再一次检查最高位
test [si+7], byte 0x80
- ; 如不为1,我们成功了,结束
+ ; 如不为 1,我们成功了,结束
jz .done_ABS
; 溢出错误,它被转成了负数
stc
- ; 设置 Carry Flag 并返回
+ ; 设置 CF 并返回
ret
.done_ABS:
- ; 成功,清理 Carry Flag 并返回
+ ; 成功,清理 CF 并返回
clc
ret
```
-你可能已经注意到了,这个函数有一个潜在问题。由于正负数的二进制值表达方式,最大负数无法被转成正数。以8位数为例,最大负数是 **-128**。如果你翻转了 **-128** 的所有位数 (二进制1__0000000),你会得到127 (二进制0__1111111) 即最大正值。如果你对结果加 **1**,它会因溢出回到同样的负数 (-128)。
+你可能已经注意到了,这个函数有一个潜在问题。由于正数和负数的二进制值表达方式,最大负数无法被转成正数。以 8 位数为例,最大负数是 `-128`。如果你翻转了 `-128` 的所有位数(二进制 `1__0000000`),你会得到 127(二进制 `0__1111111`)这个最大正值。如果你对结果加 `1`,它会因溢出回到同样的负数(`-128`)。
-你只需要重复计算绝对值的步骤就可以将正数转成负数。以下的程序十分相似,你唯一需要确认的就是一开始的数字不是已经负了。
+要将正数转成负数,你只需要重复计算绝对值的步骤就行。以下的程序十分相似,你唯一需要确认的就是一开始的数字不是已经负了。
```
; 语法, NASM for DOS
proc_NEG:
- ; 开始时,SI会指向需要转负的数字在内存里的位置。
- ; 结束时,Carry Flag永远不会被设置。
+ ; 开始时,SI 会指向需要转负的数字在内存里的位置。
+ ; 结束时,CF 永远不会被设置。
; 检查最高字节的最高位
test [si+7], byte 0x80
- ; 如为1,数已经是负数
+ ; 如为 1,数已经是负数
jnz .done_NEG
- not word [si+6] ; 翻转字的所有字位 #4
+ not word [si+6] ; 翻转字的所有位,字 #4
not word [si+4] ; 字 #3
not word [si+2] ; 字 #2
- not word [si] ; 字 #1
- inc word [si] ; 字#1 加一
- ; 如结果不为0,结束
+ not word [si] ; 字 #1
+ inc word [si] ; 字 #1 加 1
+ ; 如结果不为 0,结束
jnz .done_NEG
- ; 字#2 加一
+ ; 字 #2 加 1
inc word [si+2]
- ; 如结果为0,进位下一个字
+ ; 如结果为 0,进位下一个字
jnz .done_NEG
inc word [si+4]
jnz .done_NEG
@@ -129,17 +130,17 @@ proc_NEG:
inc word [si+6]
; 正。
.done_NEG:
- clc ; 成功,清理 Carry Flag 并返回
+ clc ; 成功,清理 CF 并返回
ret
```
-看着这些绝对值与负值函数间的通用代码,它们应该被结合起来来节约一些字节。结合代码也会带来额外的好处。首先,结合代码能帮助防止简单的笔误。这样也可以减少测试的要求。进一步来讲,这样通常会让代码变得简单易懂。在阅读一长串的汇编指令时,忘记读到哪是常有的事。现在,我们可以不管这些。
+看着这些绝对值函数与负值函数间的通用代码,它们应该被合并起来节约一些字节。合并代码也会带来额外的好处。首先,合并代码能帮助防止简单的笔误。这样也可以减少测试的要求。进一步来讲,这样通常会让代码变得简单易懂。在阅读一长串的汇编指令时,忘记读到哪里是常有的事。现在,我们可以不管这些。
计算一个数的绝对值或负值并不难。但是,这些函数对于我们即将开始的有符号整型数学运算至关重要。
-我已经覆盖了整型数字在字位层的表达的基础,也创造了可以改变这些数字的基本程序,现在我们可以做点有趣的了。
+我已经介绍了整型数字在位这一层面的基本表示方法,也创造了可以改变这些数字的基本程序,现在我们可以做点有趣的了。
-让我们来做些数学吧!
+让我们来做些数学运算吧!
--------------------------------------------------------------------------------
@@ -148,10 +149,10 @@ via: https://opensource.com/article/22/10/64-bit-math
作者:[Jerome Shidel][a]
选题:[lkxed][b]
译者:[yzuowei](https://github.com/yzuowei)
-校对:[校对者ID](https://github.com/校对者ID)
+校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/shidel
[b]: https://github.com/lkxed
-
+[0]: https://img.linux.net.cn/data/attachment/album/202212/09/150829g7c7x5e22qqo53c4.jpg
\ No newline at end of file