O.4 — 在二进制和十进制表示之间转换整数

考虑一个十进制整数值,例如 5623。我们直观地理解这些数字表示 (5 * 1000) + (6 * 100) + (2 * 10) + (3 * 1)。因为有 10 个十进制数字,所以每个后续向左的数字的值都会增加 10 倍。

二进制数的工作方式相同,只是因为只有 2 个二进制数字(0 和 1),所以每个数字的值都增加 2 倍。就像逗号通常用于使大十进制数易于阅读一样(例如 1,427,435),我们通常将二进制数写成 4 位一组,以便更容易阅读(例如 1101 0101)。

下表以十进制和二进制计数到 15

十进制值二进制值
00
11
210
311
4100
5101
6110
7111
81000
91001
101010
111011
121100
131101
141110
151111

将二进制转换为十进制

在以下示例中,我们假设处理的是无符号整数。

考虑 8 位(1 字节)二进制数 0101 1110。二进制 0101 1110 表示 (0 * 128) + (1 * 64) + (0 * 32) + (1 * 16) + (1 * 8) + (1 * 4) + (1 * 2) + (0 * 1)。如果我们将所有这些部分相加,我们得到十进制数 64 + 16 + 8 + 4 + 2 = 94。

这是表格格式的相同过程。我们将每个二进制数字乘以其数字值(由其位置决定)。将所有这些值相加即可得到总数。

将 0101 1110 转换为十进制

二进制数字0  1  0  1  1  1  1  0  
* 数字值1286432168421
= 总计 (94)0640168420

让我们将 1001 0111 转换为十进制

二进制数字1  0  0  1  0  1  1  1  
* 数字值1286432168421
= 总计 (151)12800160421

1001 0111 二进制 = 十进制 151。

这可以通过添加更多列轻松扩展到 16 位或 32 位二进制数。请注意,最简单的方法是从右端开始,然后向左工作,每次都将数字值乘以 2。

将十进制转换为二进制的方法 1

从十进制转换为二进制有点棘手,但仍然相当简单。有几种好的方法可以做到这一点。

在第一种方法中,您不断地除以 2,并记下余数。二进制数最终由余数从下到上构建。

将 148 从十进制转换为二进制(用 r 表示余数)

148 / 2 = 74 r0
74 / 2 = 37 r0
37 / 2 = 18 r1
18 / 2 = 9 r0
9 / 2 = 4 r1
4 / 2 = 2 r0
2 / 2 = 1 r0
1 / 2 = 0 r1

从下到上写下所有余数:1001 0100

十进制 148 = 二进制 1001 0100。

您可以通过将二进制数转换回十进制来验证此答案

(1 * 128) + (0 * 64) + (0 * 32) + (1 * 16) + (0 * 8) + (1 * 4) + (0 * 2) + (0 * 1) = 148

这种方法最适合人类,因为它只涉及除以 2。它对机器不太好,因为它需要存储所有计算出的位,以便以后可以反向打印。

将十进制转换为二进制的方法 2

在剩下的两种方法中,我们将向前工作,在计算每个位时进行计算,这样我们就不必在最后重新构造二进制数。

再次考虑十进制数 148。小于 148 的最大 2 的幂是 128,所以我们从这里开始。

148 >= 128 吗?是的,所以 128 位必须是 1。148 - 128 = 20,这意味着我们需要找到价值为 20 的更多位。
20 >= 64 吗?不是,所以 64 位必须是 0。
20 >= 32 吗?不是,所以 32 位必须是 0。
20 >= 16 吗?是的,所以 16 位必须是 1。20 - 16 = 4,这意味着我们需要找到价值为 4 的更多位。

4 >= 8 吗?不是,所以 8 位必须是 0。
4 >= 4 吗?是的,所以 4 位必须是 1。4 - 4 = 0,这意味着所有其余的位都必须是 0。

148 = (1 * 128) + (0 * 64) + (0 * 32) + (1 * 16) + (0 * 8) + (1 * 4) + (0 * 2) + (0 * 1) = 1001 0100

表格格式

二进制数1  0  0  1  0  1  0  0  
* 数字值1286432168421
= 总计 (148)12800160400

当数字很小(例如 8 位二进制数)时,这种方法对人类来说非常容易。它对机器也相当高效,因为每个位都需要比较、减法和赋值。

将十进制转换为二进制的方法 3

此方法是方法 2 的变体,使用整数除法。再次考虑十进制数 148。小于 148 的最大 2 的幂是 128,所以我们从这里开始。

148 / 128 = 1,有一些余数。因为 1 是奇数,所以这个位是 1。
148 / 64 = 2,有一些余数。因为 2 是偶数,所以这个位是 0。
148 / 32 = 4,有一些余数。因为 4 是偶数,所以这个位是 0。
148 / 16 = 9,有一些余数。因为 9 是奇数,所以这个位是 1。
148 / 8 = 18,有一些余数。因为 18 是偶数,所以这个位是 0。
148 / 4 = 37,有一些余数。因为 37 是奇数,所以这个位是 1。
148 / 2 = 74,有一些余数。因为 74 是偶数,所以这个位是 0。
148 / 1 = 148,有一些余数。因为 148 是偶数,所以这个位是 0。

148 = (1 * 128) + (0 * 64) + (0 * 32) + (1 * 16) + (0 * 8) + (1 * 4) + (0 * 2) + (0 * 1) = 1001 0100

这种方法对人类来说不太好,因为它需要大量的除法。它对机器也效率较低,因为除法是一种低效的操作。但是它很容易用代码编写,因为它不需要 if 语句。

另一个例子

我们用方法 1 将 117 转换为二进制

117 / 2 = 58 r1
58 / 2 = 29 r0
29 / 2 = 14 r1
14 / 2 = 7 r0
7 / 2 = 3 r1
3 / 2 = 1 r1
1 / 2 = 0 r1

从余数反向构造数字,117 = 二进制 111 0101

并使用方法 2

小于 117 的最大 2 的幂是 64。

117 >= 64 吗?是的,所以 64 位必须是 1。117 - 64 = 53。
53 >= 32 吗?是的,所以 32 位必须是 1。53 - 32 = 21。
21 >= 16 吗?是的,所以 16 位必须是 1。21 - 16 = 5。

5 >= 8 吗?不是,所以 8 位必须是 0。
5 >= 4 吗?是的,所以 4 位必须是 1。5 - 4 = 1。
1 >= 2 吗?不是,所以 2 位必须是 0。
1 >= 1 吗?是的,所以 1 位必须是 1。

十进制 117 = 二进制 111 0101。

二进制加法

在某些情况下(我们稍后会看到一个),能够将两个二进制数相加很有用。二进制数相加出奇地容易(甚至可能比十进制数相加更容易),尽管一开始可能看起来很奇怪,因为您不习惯它。

考虑两个小的二进制数
0110(十进制 6)+
0111(十进制 7)

让我们将这些相加。首先,将它们对齐,就像我们上面做的那样。然后,从右到左,我们对每一列数字进行相加,就像我们对十进制数进行相加一样。但是,由于二进制数字只能是 0 或 1,所以只有 4 种可能性

  • 0 + 0 = 0
  • 0 + 1 = 1
  • 1 + 0 = 1
  • 1 + 1 = 0,向下一列进位 1

我们来做第一列

0110 (6 in decimal) +
0111 (7 in decimal)
----
   1

0 + 1 = 1。简单。

第二列

 1
0110 (6 in decimal) +
0111 (7 in decimal)
----
  01

1 + 1 = 0,向下一列进位 1

第三列

11
0110 (6 in decimal) +
0111 (7 in decimal)
----
 101

这个有点棘手。通常,1 + 1 = 0,向下一列进位 1。但是,我们已经从上一列进位了 1,所以我们需要加 1。因此,我们这一列最终得到 1,并向下一列进位 1。

最后一列

11
0110 (6 in decimal) +
0111 (7 in decimal)
----
1101

0 + 0 = 0,但有进位 1,所以我们加 1。1101 = 十进制 13。

现在,我们如何将 1 添加到任何给定的二进制数(例如 1011 0011)?与上面相同,只是底部的数字是二进制 1。

      11  (carry column)
1011 0011 (original binary number)
0000 0001 (1 in binary)
---------
1011 0100

有符号数和补码

在上述示例中,我们只处理了无符号整数。在本节中,我们将了解如何处理有符号数(可以是负数)。

有符号整数通常使用一种称为**补码**的方法存储。在补码中,最左边(最高有效)的位用作符号位。0 符号位表示数字是正数(或零),1 符号位表示数字是负数。

正有符号数在二进制中表示方式与正无符号数相同(符号位设置为 0)。

负有符号数在二进制中表示为正数的按位反码加 1。

将十进制转换为二进制(补码)

例如,以下是我们如何在二进制补码中表示 -5

首先我们找出 5 的二进制表示:0000 0101
然后我们反转所有位:1111 1010
然后我们加 1:1111 1011

将 -76 转换为二进制

正 76 的二进制:0100 1100
反转所有位:1011 0011
加 1:1011 0100

我们为什么要加 1?考虑数字 0。如果负值仅仅表示为正数的反码(称为“反码”),那么 0 将有两种表示:0000 0000(正零)和 1111 1111(负零)。通过加 1,1111 1111 会故意溢出并变为 0000 0000。这可以防止 0 有两种表示,并简化了进行负数算术所需的一些内部逻辑。

将二进制(补码)转换为十进制

要将补码二进制数转换回十进制,首先看符号位。

如果符号位是 0,则像上面所示的无符号数一样转换该数字。

如果符号位是 1,则我们反转位,加 1,然后转换为十进制,然后将该十进制数变为负数(因为符号位最初是负数)。

例如,将 1001 1110 从补码转换为十进制数
给定:1001 1110
反转位:0110 0001
加 1:0110 0010
转换为十进制:(0 * 128) + (1 * 64) + (1 * 32) + (0 * 16) + (0 * 8) + (0 * 4) + (1 * 2) + (0 * 1) = 64 + 32 + 2 = 98
由于原始符号位是负数,所以最终值为 -98。

还有另一种更易于手动计算的方法。在这种方法中,符号位表示负值,所有其他位表示正值。

给定:1001 1110
转换为十进制:(1 * -128) + (0 * 64) + (0 * 32) + (1 * 16) + (1 * 8) + (1 * 4) + (1 * 2) + (0 * 1) = -128 + 16 + 8 + 4 + 2 = -98

类型为何重要

考虑二进制值 1011 0100。这个值代表什么?您可能会说是 180,如果这是一个无符号二进制数,您就对了。

但是,如果此值使用补码存储,则为 -76。

如果该值以其他方式编码,则可能完全是其他值。

那么 C++ 如何知道是将包含二进制 1011 0100 的变量打印为 180 还是 -76 呢?

如果本节标题没有泄露,这就是类型发挥作用的地方。变量的类型决定了变量的值如何编码为二进制,以及如何解码回值。因此,如果变量类型是无符号整数,它就会知道 1011 0100 是标准二进制,应该打印为 180。如果变量是有符号整数,它就会知道 1011 0100 是使用补码编码的(现在 C++20 保证了这一点),应该打印为 -76。

如何将浮点数转换为二进制或从二进制转换?

浮点数如何转换为二进制或从二进制转换要复杂得多,而且您可能永远不需要知道。但是,如果您好奇,请参阅此网站,它很好地详细解释了该主题。

小测验时间

问题 #1

将 0100 1101 转换为十进制。

显示答案

问题 #2

使用上面方法 1 和方法 2,将 93 转换为 8 位无符号二进制数。

显示答案

问题 #3

将 -93 转换为 8 位有符号二进制数(使用补码)。

显示答案

问题 #4

将 1010 0010 转换为无符号十进制数。

显示答案

问题 #5

将 1010 0010 转换为有符号十进制数(假设补码)。

显示答案

问题 #6

编写一个程序,要求用户输入一个介于 0 到 255 之间的数字。将此数字打印为 8 位二进制数(形式为 #### ####)。不要使用任何位运算符。不要使用 std::bitset

显示提示

显示提示

提醒:std::uint8_t 通常被视为 char,而不是 int。这在使用输入或输出时可能会导致意外行为。

显示答案

guest
您的电子邮箱地址将不会被显示
发现错误?请在上方留言!
与勘误相关的评论在处理后将被删除,以帮助减少混乱。感谢您帮助使网站对每个人都更好!
来自 https://gravatar.com/ 的头像与您提供的电子邮箱地址相关联。
有回复时通知我:  
717 评论
最新
最早 最多投票
内联反馈
查看所有评论