C++ 十进制与二进制转换

在日常生活中,我们习惯使用逢十进一的十进制,由 0~9 这 10 个数字组成。而计算机底层使用的是逢二进一的二进制,只包含 0 和 1 两个数字。

在 C++ 中编写这两个进制的转换代码时,我们通常使用字符串来保存二进制的每一位 '0' 和 '1',这样既方便逐位拼接,也便于按位遍历计算。

十进制转二进制

把一个非负十进制整数转换成二进制,最经典的方法就是 “不断除以 2 取余数”。因为二进制只有 0 和 1 两个数字,所以每次除以 2 得到的余数只可能是 0 或 1。

不妨拿十进制数 13 手算一遍。每次记录除以 2 之后的商和余数,再用商继续除以 2,直到商变成 0 为止:

13 / 2 = 6 ... 余 1
6 / 2 = 3 ... 余 0
3 / 2 = 1 ... 余 1
1 / 2 = 0 ... 余 1

按计算顺序依次得到的余数是 1、0、1、1,把它们从后往前倒过来排列,就能得到 13 对应的二进制 1101。

为什么算出来的余数要倒过来排呢?其实这和拆十进制数是一个道理:拿 123 除以 10,第 1 次取余得到的 3 是最右边的个位,最后算出来的才是百位。换成除以 2 也一样,第 1 次算出的余数是最右边的最低位,最后 1 次算出的才是最左边的最高位。

因此,我们可以配合 while 循环,把每次算出来的余数依次拼接到字符串末尾。

等循环结束后,我们再引入 <algorithm> 头文件,调用 std::reverse() 函数把字符串反转过来即可。下面来看具体实现。

示例 1:除 2 取余转二进制

#include <algorithm>        // 提供 std::reverse()
#include <iostream>
#include <string>

// 将十进制非负整数转换为二进制字符串
std::string decimalToBinary(int n) {
    if (n == 0) {
        return "0";         // 单独处理 0 的边界情况
    }

    std::string bin = "";
    while (n > 0) {
        int rem = n % 2;    // 取出除以 2 的余数
        bin += (rem == 0 ? '0' : '1');
        n /= 2;             // 商作为下一轮的被除数
    }

    // 余数从低位到高位记录,最后反转字符串
    std::reverse(bin.begin(), bin.end());
    return bin;
}

int main() {
    std::cout << "0 的二进制是:" << decimalToBinary(0) << '\n';
    std::cout << "13 的二进制是:" << decimalToBinary(13) << '\n';
    std::cout << "42 的二进制是:" << decimalToBinary(42) << '\n';
    return 0;
}

运行结果如下。

0 的二进制是:0
13 的二进制是:1101
42 的二进制是:101010

分析:

当我们向 decimalToBinary() 传入 0 时,函数开头的 if (n == 0) 会直接返回字符串 "0"。这是因为如果 n 一开始就是 0,后面的 while (n > 0) 条件不成立,循环体一次都不会执行,不提前处理就会返回空字符串 ""。

当 n 大于 0 时,循环内先通过 int rem = n % 2; 算出余数,再转为字符 '0' 或 '1' 拼到 bin 末尾。由于 '0' 和 '1' 的编码是连续的,这里的拼接语句也可以写成 bin += static_cast<char>('0' + rem);。

比如传入 13 时,4 轮循环结束后 bin 里保存的是反过来的 "1011"。最后调用 std::reverse() 掉转高低位顺序,就能得到正确的 "1101"。

提示 在实际开发中,如果只需要把整数转换为 8 位或 16 位等固定长度的二进制字符串,我们也可以直接使用 <bitset> 头文件中的 std::bitset。例如写 std::bitset<8>(13).to_string(),就能得到补齐前导零的 8 位二进制字符串 "00001101"。

二进制转十进制

反过来,如果我们拿到了一个二进制字符串(比如 "1101"),应该怎样把它还原成十进制整数呢?

先想一想平时熟悉的十进制:从左往右读字符串 "123" 时,每往右多看一位,前面算出的结果就要乘以 10 再加上新的一位。二进制是逢二进一,道理完全一样:我们先准备结果变量 result = 0,从左往右逐个读取字符,每读入一位就把 result 乘以 2 再加上当前位。

拿 "1101" 来说,result 初始值为 0,从左往右读入 4 个字符的计算过程如下:

读入 '1':result = 0 * 2 + 1 = 1
读入 '1':result = 1 * 2 + 1 = 3
读入 '0':result = 3 * 2 + 0 = 6
读入 '1':result = 6 * 2 + 1 = 13

小伙伴们要注意,从字符串里取出来的 '0' 和 '1' 属于字符类型(char),不能直接当成整数 0 和 1 相加。借助前面学过的 char 与 int 互转方法,我们只需拿数字字符减去字符 '0'(即写成 ch - '0'),就能得到对应的整数 0 或 1。

下面我们配合范围 for 循环从左往右遍历二进制字符串,把整个转换过程写成代码。

示例 2:按位乘 2 转十进制

#include <iostream>
#include <string>

// 将二进制字符串转换为十进制整数
int binaryToDecimal(const std::string& bin) {
    int result = 0;
    for (char ch : bin) {
        int bit = ch - '0';           // 字符转为数字 0 或 1
        result = result * 2 + bit;    // 乘 2 再加上当前位
    }
    return result;
}

int main() {
    std::string b1 = "1101";
    std::string b2 = "101010";

    std::cout << b1 << " 转为十进制是:" << binaryToDecimal(b1) << '\n';
    std::cout << b2 << " 转为十进制是:" << binaryToDecimal(b2) << '\n';
    return 0;
}

运行结果如下。

1101 转为十进制是:13
101010 转为十进制是:42

分析:

int bit = ch - '0'; 这句代码表示把当前读取的字符 '0' 或 '1' 转换成对应的整数 0 或 1,刚好和前面的 '0' + rem 互为逆操作。紧接着 result = result * 2 + bit; 将前面的结果乘 2 并加上当前位,不需要调用幂函数计算 2 的几次方,写起来非常简洁。

对于上面这个例子,小伙伴们可以把测试字符串改成带有前导零的 "001101" 运行试试,看看算出来的结果为什么依然是 13。

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号