O-4 二进制与十进制之间的整数转换

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

二进制数字的运作原理相同,但由于仅有两个二进制位(0和1),因此每个数字的值以2为单位递增。正如用逗号分隔可使大数更易读(如1,427,435),二进制数也常以4位为一组书写(如1101 0101)。

下表展示十进制与二进制计数至15的过程:

Decimal Value Binary Value
0 0
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
9 1001
10 1010
11 1011
12 1100
13 1101
14 1110
15 1111

二进制转十进制

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

考虑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 转换为十进制:

Binary digit 0 1 0 1 1 1 1 0
* Digit value 128 64 32 16 8 4 2 1
= Total (94) 0 64 0 16 8 4 2 0

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

Binary digit 1 0 0 1 0 1 1 1
* Digit value 128 64 32 16 8 4 2 1
= Total (151) 128 0 0 16 0 4 2 1

1001 0111 二进制数 = 151 十进制数。

只需增加更多列,即可轻松扩展为16位或32位二进制数。请注意,最简便的方法是从右端开始,逐步向左推进,同时将各位数值乘以2。


十进制转二进制方法一

十进制转二进制稍复杂些,但依然相当直观。存在几种有效的转换方法。

第一种方法是持续进行除以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的运算。但对机器而言效率较低,因为需要在计算过程中存储所有位,以便后续按逆序输出。


方法二:十进制转二进制

在剩余两种方法中,我们将采用正向计算方式,逐位推导结果,从而避免最终重建二进制数。

再次以十进制数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

表格形式如下:

Binary number 1 0 0 1 0 1 0 0
* Digit value 128 64 32 16 8 4 2 1
= Total (148) 128 0 0 16 0 4 0 0

当数字较小(例如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 余1
58 / 2 = 29 余0
29 / 2 = 14 余1
14 / 2 = 7 余0
7 / 2 = 3 余1
3 / 2 = 1 余1
1 / 2 = 0 余1

从低位开始用余数构造二进制数:117 = 111 0101

方法二:
小于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两种可能,实际只有四种情况:

  • 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。
那么,如何给任意二进制数(如1011 0011)加1呢?方法与上述相同,只是底数变为二进制1。

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

有符号数与二进制补码

在前面的示例中,我们仅处理了无符号整数。本节将探讨如何处理有符号数(可包含负数)。

有符号整数通常采用二进制补码法two’s complement存储。在二进制补码中,最左侧(最高位)的位被用作符号位。当符号位为0时表示数值为正(或零);当符号位为1时表示数值为负。

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

负有符号数在二进制中的表示形式为:取正数二进制表示的按位取反,再加1。


十进制转二进制(补码法)

例如,以下是-5的二进制补码表示法:

首先求出5的二进制表示:0000 0101

然后将所有位取反:1111 1010

最后加1:1111 1011

将-76转换为二进制:

正数76的二进制表示:0100 1100

反转所有位:1011 0011

加1:1011 0100

为何要加1?以数字0为例。若负值仅表示为正数的反码(即“1的补码”),则0将存在两种表示形式:0000 0000(正零)和1111 1111(负零)。通过加1操作,1111 1111会故意溢出并变为0000 0000。这既避免了0的双重表示,又简化了负数运算所需的部分内部逻辑。


为什么类型很重要

考虑二进制值 1011 0100。它代表什么数值?你可能会说 180,如果这是无符号二进制数,你的答案是正确的。

然而,如果该值采用补码存储,则表示 -76。

若采用其他编码方式,其表示的数值可能完全不同。

那么C++如何判断应将二进制1011 0100变量输出为180还是-76?

正如章节标题所示,类型在此发挥关键作用。变量的类型决定了其值如何编码为二进制,以及如何解码回具体数值。若变量类型为无符号整数,系统会识别1011 0100为标准二进制,应输出为180。若变量为有符号整数,则会判定1011 0100采用补码编码(C++20起强制规定),应输出为-76。


那么浮点数与二进制之间的转换呢?

浮点数与二进制之间的转换过程相当复杂,通常您可能永远不需要了解具体细节。不过若您感兴趣,可以参考这个网站,它对该主题进行了详细而清晰的阐述。


测验时间

问题 #1

将 0100 1101 转换为十进制数。

显示解答

Binary digit 0 1 0 0 1 1 0 1
* Digit value 128 64 32 16 8 4 2 1
= Total (77) 0 64 0 0 8 4 0 1
答案是77。

问题 #2

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

显示解答

使用方法1:
93 / 2 = 46 余1
46 / 2 = 23 余0
23 / 2 = 11 余1
11 / 2 = 5 余1
5 / 2 = 2 余1
2 / 2 = 1 余0
1 / 2 = 0 余1
从余数逆向推算:101 1101
方法二:
小于93的最大2的幂是64。
93是否大于等于64?是,因此64位为1。93 - 64 = 29。
29 ≥ 32?否,故32位为0。
29 ≥ 16?是,故16位为1。29 - 16 = 13。
13 ≥ 8?是,故8位为1。13 - 8 = 5。
5 ≥ 4?是,因此4位为1。5 - 4 = 1。
1 ≥ 2?否,因此2位为0。
1 ≥ 1?是,因此1位为1。
答案为0101 1101。

问题 #3

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

显示解答

我们已知93的二进制形式为0101 1101(来自前文解答)。

采用二进制补码法时,需将各位取反:1010 0010

再加1:1010 0011

问题 #4

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

显示解答

从右向左计算:
1010 0010 = (0 × 1) + (1 × 2) + (0 × 4) + (0 × 8) + (0 × 16) + (1 × 32) + (0 × 64) + (1 × 128) = 2 + 32 + 128 = 162。
答案是162。

问题 #5

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

显示解答

既然我们知道这个数是二进制补码形式,可以通过反转位并加1来“还原”二进制补码。
首先,从二进制数开始:1010 0010
反转位序:0101 1101
加1:0101 1110
转换为十进制:64 + 16 + 8 + 4 + 2 = 94
注意这是二进制补码形式,且原始左位为负值:-94
答案为-94

问题 #6

编写程序:要求用户输入 0 至 255 之间的数值。将该数值以8位二进制形式(格式为 #### ####)输出。禁止使用位运算符,禁止使用std::bitset。

显示提示

提示:使用方法3。假设最大的2的幂为128。

显示提示

提示:你可以使用取模运算符(%)来判断一个数是偶数还是奇数。

提醒:std::uint8_t通常被视为char类型而非int类型,在输入输出操作中可能导致意外行为。

显示解答

#include <iostream>

void printBit(int x, int pow)
{
    std::cout << ((x / pow) % 2);
}

void print8BitBinary(int x)
{
    printBit(x, 128);
    printBit(x, 64);
    printBit(x, 32);
    printBit(x, 16);

    std::cout << ' ';

    printBit(x, 8);
    printBit(x, 4);
    printBit(x, 2);
    printBit(x, 1);
}

int main()
{
    std::cout << "Enter an integer between 0 and 255: ";
    int x{};
    std::cin >> x;

    print8BitBinary(x);

    std::cout << '\n';

    return 0;
}
posted @ 2026-02-21 10:07  游翔  阅读(0)  评论(0)    收藏  举报