单元 1.4.1 数据类型
当我们存储数据时,我们为它分配一个特定的类型。我们用于数据片段的类型会影响我们如何存储和处理它。
你需要了解以下类型。
类型 | 描述 | 例子 | |||
---|---|---|---|---|---|
整数 | 一个整数(不是分数,没有小数点) | 3 | -12 | 17 | 0 |
实数 | 有小数部分或分数的数字 | 17.4 | 3.14 | -4.8 | 0.05 |
字符 | 单个字母、数字或其他字符 | 4 | y | J | % |
字符串 | 多个字符的运行 | aj%kD | Matt | bar | 12three |
布尔值 | 两种状态之一(开或关) | 真 | 假 |
数字可以用多种进制表示,人类碰巧在使用数字时使用十进制(或十进制)。
让我们以数字 184 为例。我们可以把它分成单位、十位和百位,如下所示
100 | 10 | 1 |
---|---|---|
1 | 8 | 4 |
我们放在单元格中的是我们要将列加多少次才能得到我们的数字。在这里,我们想要一个百位,八个十位和四个一位。
我们可以看到,每列都增加 10 的倍数,这是我们表示数字的进制。
让我们看看如何在二进制(二进制)中工作,以数字 14 为例。
- 2 的最高次方,适合 14 是 8 (23),.
- 2 的最高次方,适合 6 是 4 (22),.
- 这让我们剩下 2,这是我们的其中一列。
这给了我们 14 的二进制形式。
8 | 4 | 2 | 1 |
---|---|---|---|
1 | 1 | 1 | 0 |
在上面的示例中,我们使用了 4 位(即我们有 4 列,我们可以填充它们来表示该数字)。与十进制一样,更大的数字需要更多的列,因此在接下来的示例中,我们将使用 8 位。
从数字 215 开始。
因此,215 可以写成 .
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
---|---|---|---|---|---|---|---|
1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 |
用二进制表示负数是一个更复杂的过程,因为我们需要一种方法来知道一个数字是负数还是正数,而只能使用 1 和 0,而不将其与数字本身混淆。有两种主要方法用于表示这些数字:符号和大小以及补码
这是在二进制中表示负数的最简单方法,它只涉及使用数字的第一位来表示该数字是负数还是正数。如果数字为负,则它为 1,否则为 0。
例如
数字 | 二进制 |
---|---|
67 | 01000011
|
-67 | 11000011
|
然而,这在用这些数字进行算术运算时也给我们带来了一些问题,因为如果我们把 67 和 -67 相加,我们应该得到 0。然而,如下所示,情况并非如此。
0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | |
+ | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
= | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
进位 | 1 | 1 | 1 | 1 |
这等于 6,由于溢出导致丢失一位。这意味着符号和大小不能自动进行计算,数字必须在使用之前解码。
补码是另一种表示负数的方法,旨在解决这个问题。转换数字的方法稍微复杂一些,但它更强大。如果我们要表示数字 -67,就像上面一样,我们遵守以下步骤
- 在数字前面添加若干空位,以常规二进制表示数字
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
- 对数字取反(将所有 1 转换为 0,将所有 0 转换为 1)
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | |
反转 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 0 |
- 将数字加 1
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
---|---|---|---|---|---|---|---|---|
1 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | |
+ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
= | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 |
就是这样。现在该数字以补码形式表示 -67。
使用补码的主要优点之一是能够在不进行转换的情况下进行数学运算。如果我们以 67 加 -67 为例
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | |
+ | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 |
= | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
进位 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
因此等于 1,由于溢出导致丢失一位。这对于常规减法(将正数加到负数以模拟减法)也适用。因此,这在更常见的情况下使用,并且可以更容易地实现。
用二进制表示小数比负数更复杂,因为我们需要以某种方式存储小数点的位置,同时还要保持我们得到的数字和精度。您需要了解的这种方法是标准化浮点表示。
这种方法对系统上存储的所有小数使用标准格式。它使用类似于我们使用科学记数法表示数字的方法(345.54 变为 ),通过将小数点定义在第一位和第二位之间,并存储数字移动了多少位。数字本身称为尾数,具有固定位数,移动的位数称为指数,同样具有固定大小。
使用这种方法时需要注意的一点是,您必须清楚地了解用于存储数字每一位的位数。当处理非常大的数字或具有很多小数位的数字时,这一点变得非常重要,因为您将开始遇到精度丢失的错误(浮点错误)。
要以标准形式表示小数,我们使用之前用过的列,但在 1 列的右侧,我们添加一个小数点,并使用 形式继续列。
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | . | |||
---|---|---|---|---|---|---|---|---|---|---|---|
从这里我们可以直接表示我们的数字。对于这个例子,我们将表示数字 47.625。整数部分可以像往常一样表示为 ,小数部分可以表示为 。
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | . | |||
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | . | 1 | 0 | 1 |
所以现在我们有了数字,但我们永远无法将它存储在设备上,因为我们在中间有一个小数点。这就是浮点概念的来源。我们需要找到二进制中数字第一次变化的位置。这意味着数字从 0 变为 1 或反之的第一点。然后我们在这两个值之间放置小数点,并记录我们移动了多少位小数点。
128 | 64 | . | 32 | 16 | 8 | 4 | 2 | 1 | |||
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | . | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
. | ← | ← | ← | ← | ← | ← |
我们将其放置在 64 和 32 之间,因为这是数字中从 0 变为 1 的第一次变化,现在它是标准化形式。然后我们将它向左移动了六位。在这个例子中,我们将使用 10 位尾数和 6 位指数(存储在 16 位寄存器中)。使用标准化形式时,我们只取虚数小数点前的一位数字,然后在数字的右侧使用 0。这意味着我们在这里表示的数字在使用 10 位时变为 01011111010
。然后我们将它向左移动了 6 位,这意味着正向移动了 6 位。这是 6 位二进制中的 000110
。
然后我们可以将数字附加在一起
01011111010
000110
这成功地在标准化浮点中表示了小数 47.625。这种方法也可以与补码结合使用来表示负小数,它可以用来表示非常小的数字,例如 0.00005,但是,您必须记住,向右移动是负数,向左移动是正数。
就像十进制加法一样,我们将两个数字的列对齐,然后从右边开始添加值。
- 1 加 1 等于 2(因此有 0 个 1 和 1 个 2),所以 1 的列为 0,并将 1 进位到 2 的列。
- 1 加 0 加 1 等于 2,所以设置为 0,并再次进位。
- 1 加 1 加 1 等于 3,所以设置为 1,并进位。
- 0 加 0 加 1 等于 1。
- 1 加 1 等于 2,所以设置为 0,进位 1。
- 0 加 0 加 1 等于 1。
- 1 加 0 等于 1。
- 1 加 0 等于 1。
上面的计算过程可以在表格中更加清晰地看到。
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
---|---|---|---|---|---|---|---|---|
1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | |
+ | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
= | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 0 |
进位 | 1 | 1 | 1 | 1 |
检查十进制结果:
十六进制是另一种在计算机中经常使用的数字表示形式。它主要用于表示比二进制或十进制更大的数字,但使用更少的位数(尽管它始终以二进制形式存储在计算机内存中)。
十六进制是 16 进制的名称,这意味着我们使用 16 个字符来表示数字。由于我们只有 10 个数字,所以我们使用字符 A
、B
、C
、D
、E
和 F
来分别表示 10、11、12、13、14 和 15。
在 16 进制中表示十进制数时,我们使用与二进制相同的规则;但是,我们不能将列写成 2 的幂,而是写成 16 的幂。例如,要将 7652 表示为十六进制,
- 4096 () 可以进入 7652 一次,这意味着我们在该列中放置一个
1
,现在还剩下 3556。 - 256 () 可以进入 3556 13 次,这意味着我们在该列中放置一个
D
,现在还剩下 228。 - 16 () 可以进入 228 14 次,这意味着我们在该列中放置一个
E
,现在还剩下 4。 - 1 () 可以进入 4 4 次,这意味着我们在该列中放置一个
4
,现在还剩下 0。
4096 | 256 | 16 | 1 |
---|---|---|---|
1 | D | E | 4 |
因此,7652
在十六进制中为 1DE4
。
将二进制直接转换为十六进制实际上是一个简单的过程,不需要一次性将整个二进制数转换为十进制。相反,在进行这种转换时,我们只需将二进制数按每 4 位(一个字节)为单位转换为十进制,然后转换为十六进制。如果我们有二进制数 156(10011100
),我们可以将其放入表格中并写出正确的标题,如所示:
8 | 4 | 2 | 1 | 8 | 4 | 2 | 1 |
---|---|---|---|---|---|---|---|
1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 |
= 9 | = 12 | ||||||
= 9 | = C |
因此,该数字在十六进制中为 9C
。
位操作是指对二进制数的位进行的一系列特定操作。规格说明中需要了解的操作包括:移位、与、或和异或。一旦理解了,这是一个非常快速简便的主题。
二进制寄存器可以向左或向右移位,顾名思义,它只是将二进制位向一个方向移动。在大多数情况下,任何产生的空格将用 0 填充。
左移将寄存器的内容移到左边,并丢弃任何不再适合寄存器的位。此功能很重要,因为它相当于将一个数字乘以 2。在此示例中,我们将使用值为 54 的值:
因此,如果我们在保持最大大小为 8 位的情况下进行左移(通常用字符 <<
表示)
00110110 << 1 = 01101100 = 108
00110110 << 2 = 11011000 = 216
00110110 << 3 = 10110000 = 176
正如您在最后一个示例中看到的,当我们移位 3 位时,第一个位被“推出”寄存器并丢失了。在使用左移时,这是一个重要的考虑因素,因为我们必须注意何时会丢失潜在的重要位。在前两个示例中,您可以看到每次左移都等于将原始十进制数乘以 2 的幂。左移一位乘以 2,左移两位乘以 4。如果我们使用更大的寄存器,则左移三位将使我们得到原始值乘以 8。
此功能通常用于将数字相乘,因为它可以分解为乘以 3 的幂,然后将原始数字加若干次。例如,将一个数字乘以 5 等于左移两位(乘以 4),然后再次加上原始数字。
毫不奇怪,右移与左移相反,而是将寄存器的内容移到右边。本节不会详细介绍,因为您应该已经了解移位的基本原理以及如何使用它。
如果我们在保持最大大小为 8 位的情况下进行右移(通常用字符 >>
表示)
00110110 >> 1 = 00011011 = 27
00110110 >> 2 = 00001101 = 13
00110110 >> 3 = 00000110 = 6
从第二个示例中,下溢错误的影响立即显而易见。
您可能没有完全了解 与门,因为这在单元 1.4.3 中出现,但是您应该能够猜到。如果两个输入都为 1,则与门返回 1 值。
与可以用作位运算符,将两个二进制寄存器或二进制值进行与运算。例如,如果我们有两个寄存器 01101000
和 01000010
,我们可以进行
01101000 & 01000010 = 01000000
此操作的一个应用是确定一个数字是否能被 2 整除。如果我们将任何值与二进制值 1
进行与运算,它将只留下最低有效位(最后一位)。如果这个数字大于 0,则它是奇数,否则它是偶数。
或门如果两个输入之一为 1,则返回 1 值。这可以用作位运算符,将两个值进行或运算。为了使用上面的示例
01101000 | 01000010 = 01101010
此操作的一个应用是在二进制数中存储一系列真值或假值,例如对于非常简单的控制程序。如果用户想在系统尚未启用时启用它,他们可以将存储状态的寄存器的内容与在对应于设置的位置包含 1 的二进制数进行或运算。结果值将包含原始设置,如果该位尚未设置为 1,则该位将设置为 1。
异或门是或门的修改版本,如果两个输入之一为 1,则返回 1,但如果两个输入都为 1,则返回 0。为了使用上面的示例
01101000 ^ 01000010 = 00101010
这可以再次应用于控制系统以切换设置,因为如果该位当前在寄存器中关闭,它将被启用,但如果它处于打开状态,它将被切换到关闭状态。
表示是一个相对简单的过程,它使用数字来表示符号。虽然过程本身很简单,但实现实际上可能更复杂。为了使多台机器能够通信文本,它们必须有一个标准来定义哪些数字用于哪些字符。这些标准称为字符集,在字符的定义方式和可用的大小方面差异很大。
有许多不同的字符集在不同的地方被实现,系统甚至可以使用多个字符集,只要它们知道当前使用的是哪一个。您可以查看 此处 的常见字符集列表。但是,您只需要知道两种字符集的存在:ASCII 和 Unicode。
ASCII 代表美国信息交换标准代码。它是在 1963 年定义的,是使用最广泛的编码之一。它默认使用 7 位来表示字符,这使得最多可以表示 128 个字符。此标准还有一些扩展,例如扩展 ASCII,它使用 8 位来表示字符,从而增加了可能的选项。侧边显示了所有 ASCII 代码及其对应十进制值的表格。当文本使用此编码进行编码并存储时,每个十进制数字都以二进制形式表示并存储。
Unicode 的功能与 ASCII 相同,但它在用于存储字符的位数上有所不同。Unicode 有多个子集,具有不同的字符数,例如 UTF-8
、UTF-16
和 UTF-32
。最新的 Unicode 标准(截至撰写本文时)有 128,237 个可能的字符。它已成为系统的最新标准,并在构建新系统时使用,以便适应更广泛的字符和语言。
侧边显示了一个图像,显示了一部分可能的 Unicode 符号。您可以在维基百科的 Unicode 页面上阅读有关 Unicode 的更多信息。