MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

C++ 多进制间互转换

2021-05-181.3k 阅读

1. 引言

在计算机编程领域,不同进制数的转换是一项基础且重要的操作。C++ 作为一种强大的编程语言,提供了丰富的工具和方法来实现多进制间的相互转换。本文将深入探讨 C++ 中各种进制(二进制、八进制、十进制、十六进制)之间的转换原理与实现方式,并通过详细的代码示例帮助读者更好地理解和掌握。

2. 基础知识回顾

2.1 十进制数

十进制是我们日常生活中最常用的计数系统,它使用 10 个数字(0 - 9)来表示数值。例如,数字 123 表示为: [1 \times 10^2 + 2 \times 10^1 + 3 \times 10^0 = 100 + 20 + 3 = 123]

2.2 二进制数

二进制数只使用两个数字 0 和 1,它是计算机内部存储和处理数据的基础进制。例如,二进制数 101 表示为: [1 \times 2^2 + 0 \times 2^1 + 1 \times 2^0 = 4 + 0 + 1 = 5_{(10)}]

2.3 八进制数

八进制数使用 8 个数字(0 - 7)来表示数值。例如,八进制数 12 表示为: [1 \times 8^1 + 2 \times 8^0 = 8 + 2 = 10_{(10)}]

2.4 十六进制数

十六进制数使用 16 个数字和字母(0 - 9,A - F)来表示数值,其中 A 表示 10,B 表示 11,以此类推,F 表示 15。例如,十六进制数 1A 表示为: [1 \times 16^1 + A \times 16^0 = 1 \times 16 + 10 \times 1 = 16 + 10 = 26_{(10)}]

3. 十进制与其他进制的转换

3.1 十进制转二进制

3.1.1 原理

十进制转二进制采用“除 2 取余,逆序排列”的方法。具体步骤如下:

  1. 将十进制数除以 2,记录余数。
  2. 将商继续除以 2,直到商为 0。
  3. 将所有的余数从下往上排列,得到二进制数。

3.1.2 代码示例

#include <iostream>
#include <vector>

std::string decimalToBinary(int decimal) {
    std::vector<int> binaryDigits;
    while (decimal > 0) {
        binaryDigits.push_back(decimal % 2);
        decimal /= 2;
    }
    std::string binary;
    for (auto it = binaryDigits.rbegin(); it != binaryDigits.rend(); ++it) {
        binary += std::to_string(*it);
    }
    return binary;
}

int main() {
    int decimal = 10;
    std::cout << "Decimal " << decimal << " in binary is " << decimalToBinary(decimal) << std::endl;
    return 0;
}

3.2 十进制转八进制

3.2.1 原理

十进制转八进制采用“除 8 取余,逆序排列”的方法。具体步骤与转二进制类似,只是除数变为 8。

3.2.2 代码示例

#include <iostream>
#include <vector>

std::string decimalToOctal(int decimal) {
    std::vector<int> octalDigits;
    while (decimal > 0) {
        octalDigits.push_back(decimal % 8);
        decimal /= 8;
    }
    std::string octal;
    for (auto it = octalDigits.rbegin(); it != octalDigits.rend(); ++it) {
        octal += std::to_string(*it);
    }
    return octal;
}

int main() {
    int decimal = 10;
    std::cout << "Decimal " << decimal << " in octal is " << decimalToOctal(decimal) << std::endl;
    return 0;
}

3.3 十进制转十六进制

3.3.1 原理

十进制转十六进制采用“除 16 取余,逆序排列”的方法。在获取余数时,如果余数大于 9,则需要将其转换为对应的字母(10 - A,11 - B,...,15 - F)。

3.3.2 代码示例

#include <iostream>
#include <vector>
#include <string>

std::string decimalToHexadecimal(int decimal) {
    std::vector<char> hexDigits;
    while (decimal > 0) {
        int remainder = decimal % 16;
        if (remainder < 10) {
            hexDigits.push_back(static_cast<char>(remainder + '0'));
        } else {
            hexDigits.push_back(static_cast<char>(remainder - 10 + 'A'));
        }
        decimal /= 16;
    }
    std::string hexadecimal;
    for (auto it = hexDigits.rbegin(); it != hexDigits.rend(); ++it) {
        hexadecimal += *it;
    }
    return hexadecimal;
}

int main() {
    int decimal = 26;
    std::cout << "Decimal " << decimal << " in hexadecimal is " << decimalToHexadecimal(decimal) << std::endl;
    return 0;
}

3.4 二进制转十进制

3.4.1 原理

二进制转十进制是根据二进制数的位权展开求和。对于二进制数 (a_{n}a_{n - 1}...a_{0}),其对应的十进制数为: [a_{n} \times 2^{n} + a_{n - 1} \times 2^{n - 1} +... + a_{0} \times 2^{0}]

3.4.2 代码示例

#include <iostream>
#include <string>

int binaryToDecimal(const std::string& binary) {
    int decimal = 0;
    int power = 1;
    for (auto it = binary.rbegin(); it != binary.rend(); ++it) {
        if (*it == '1') {
            decimal += power;
        }
        power *= 2;
    }
    return decimal;
}

int main() {
    std::string binary = "101";
    std::cout << "Binary " << binary << " in decimal is " << binaryToDecimal(binary) << std::endl;
    return 0;
}

3.5 八进制转十进制

3.5.1 原理

八进制转十进制同样是根据位权展开求和。对于八进制数 (a_{n}a_{n - 1}...a_{0}),其对应的十进制数为: [a_{n} \times 8^{n} + a_{n - 1} \times 8^{n - 1} +... + a_{0} \times 8^{0}]

3.5.2 代码示例

#include <iostream>
#include <string>

int octalToDecimal(const std::string& octal) {
    int decimal = 0;
    int power = 1;
    for (auto it = octal.rbegin(); it != octal.rend(); ++it) {
        int digit = *it - '0';
        decimal += digit * power;
        power *= 8;
    }
    return decimal;
}

int main() {
    std::string octal = "12";
    std::cout << "Octal " << octal << " in decimal is " << octalToDecimal(octal) << std::endl;
    return 0;
}

3.6 十六进制转十进制

3.6.1 原理

十六进制转十进制也是根据位权展开求和。对于十六进制数 (a_{n}a_{n - 1}...a_{0}),其对应的十进制数为: [a_{n} \times 16^{n} + a_{n - 1} \times 16^{n - 1} +... + a_{0} \times 16^{0}],其中 A - F 分别对应 10 - 15。

3.6.2 代码示例

#include <iostream>
#include <string>

int hexadecimalToDecimal(const std::string& hexadecimal) {
    int decimal = 0;
    int power = 1;
    for (auto it = hexadecimal.rbegin(); it != hexadecimal.rend(); ++it) {
        int digit;
        if (*it >= '0' && *it <= '9') {
            digit = *it - '0';
        } else if (*it >= 'A' && *it <= 'F') {
            digit = *it - 'A' + 10;
        } else if (*it >= 'a' && *it <= 'f') {
            digit = *it - 'a' + 10;
        }
        decimal += digit * power;
        power *= 16;
    }
    return decimal;
}

int main() {
    std::string hexadecimal = "1A";
    std::cout << "Hexadecimal " << hexadecimal << " in decimal is " << hexadecimalToDecimal(hexadecimal) << std::endl;
    return 0;
}

4. 非十进制之间的转换

4.1 二进制与八进制的转换

4.1.1 二进制转八进制原理

由于 (2^3 = 8),所以可以将二进制数从右到左每三位一组进行划分,不足三位的在左边补 0,然后将每组三位二进制数转换为对应的八进制数字。例如,二进制数 10110 可以划分为 010 和 110,分别对应八进制的 2 和 6,所以二进制 10110 转换为八进制是 26。

4.1.2 代码示例

#include <iostream>
#include <string>

std::string binaryToOctal(const std::string& binary) {
    std::string octal;
    int len = binary.length();
    if (len % 3 != 0) {
        std::string paddedBinary = std::string(3 - len % 3, '0') + binary;
        len = paddedBinary.length();
    } else {
        std::string paddedBinary = binary;
    }
    for (int i = 0; i < len; i += 3) {
        int group = (paddedBinary[i] - '0') * 4 + (paddedBinary[i + 1] - '0') * 2 + (paddedBinary[i + 2] - '0');
        octal += std::to_string(group);
    }
    return octal;
}

std::string octalToBinary(const std::string& octal) {
    std::string binary;
    for (char digit : octal) {
        int num = digit - '0';
        std::string group;
        for (int i = 2; i >= 0; --i) {
            group += (num & (1 << i))? '1' : '0';
        }
        binary += group;
    }
    while (binary.length() > 1 && binary[0] == '0') {
        binary.erase(0, 1);
    }
    return binary;
}

int main() {
    std::string binary = "10110";
    std::cout << "Binary " << binary << " in octal is " << binaryToOctal(binary) << std::endl;
    std::string octal = "26";
    std::cout << "Octal " << octal << " in binary is " << octalToBinary(octal) << std::endl;
    return 0;
}

4.2 二进制与十六进制的转换

4.2.1 二进制转十六进制原理

因为 (2^4 = 16),所以将二进制数从右到左每四位一组进行划分,不足四位的在左边补 0,然后将每组四位二进制数转换为对应的十六进制数字。例如,二进制数 110110 划分为 0011 和 0110,分别对应十六进制的 3 和 6,所以二进制 110110 转换为十六进制是 36。

4.2.2 代码示例

#include <iostream>
#include <string>

std::string binaryToHexadecimal(const std::string& binary) {
    std::string hexadecimal;
    int len = binary.length();
    if (len % 4 != 0) {
        std::string paddedBinary = std::string(4 - len % 4, '0') + binary;
        len = paddedBinary.length();
    } else {
        std::string paddedBinary = binary;
    }
    for (int i = 0; i < len; i += 4) {
        int group = (paddedBinary[i] - '0') * 8 + (paddedBinary[i + 1] - '0') * 4 + (paddedBinary[i + 2] - '0') * 2 + (paddedBinary[i + 3] - '0');
        if (group < 10) {
            hexadecimal += std::to_string(group);
        } else {
            hexadecimal += static_cast<char>(group - 10 + 'A');
        }
    }
    return hexadecimal;
}

std::string hexadecimalToBinary(const std::string& hexadecimal) {
    std::string binary;
    for (char digit : hexadecimal) {
        int num;
        if (digit >= '0' && digit <= '9') {
            num = digit - '0';
        } else if (digit >= 'A' && digit <= 'F') {
            num = digit - 'A' + 10;
        } else if (digit >= 'a' && digit <= 'f') {
            num = digit - 'a' + 10;
        }
        std::string group;
        for (int i = 3; i >= 0; --i) {
            group += (num & (1 << i))? '1' : '0';
        }
        binary += group;
    }
    while (binary.length() > 1 && binary[0] == '0') {
        binary.erase(0, 1);
    }
    return binary;
}

int main() {
    std::string binary = "110110";
    std::cout << "Binary " << binary << " in hexadecimal is " << binaryToHexadecimal(binary) << std::endl;
    std::string hexadecimal = "36";
    std::cout << "Hexadecimal " << hexadecimal << " in binary is " << hexadecimalToBinary(hexadecimal) << std::endl;
    return 0;
}

4.3 八进制与十六进制的转换

4.3.1 原理

八进制与十六进制之间的转换通常借助十进制作为中间进制。即先将八进制转换为十进制,再将十进制转换为十六进制;或者先将十六进制转换为十进制,再将十进制转换为八进制。

4.3.2 代码示例

#include <iostream>
#include <string>

std::string octalToHexadecimal(const std::string& octal) {
    int decimal = octalToDecimal(octal);
    return decimalToHexadecimal(decimal);
}

std::string hexadecimalToOctal(const std::string& hexadecimal) {
    int decimal = hexadecimalToDecimal(hexadecimal);
    return decimalToOctal(decimal);
}

int main() {
    std::string octal = "26";
    std::cout << "Octal " << octal << " in hexadecimal is " << octalToHexadecimal(octal) << std::endl;
    std::string hexadecimal = "1A";
    std::cout << "Hexadecimal " << hexadecimal << " in octal is " << hexadecimalToOctal(hexadecimal) << std::endl;
    return 0;
}

5. 处理大整数的进制转换

在实际应用中,可能会遇到需要处理超出基本数据类型范围的大整数的进制转换。在 C++ 中,可以使用库如 GMP(GNU Multiple Precision Arithmetic Library)来处理大整数。

5.1 GMP 库简介

GMP 是一个用于处理任意精度算术的开源库。它提供了丰富的函数来进行大整数的加、减、乘、除以及进制转换等操作。

5.2 安装 GMP 库

在不同的操作系统上安装 GMP 库的方法略有不同。

  • Linux:通常可以使用包管理器进行安装,例如在 Ubuntu 上可以使用 sudo apt - get install libgmp3 - dev 命令安装。
  • Windows:可以从 GMP 官方网站下载预编译的库文件,并按照官方文档进行配置。

5.3 使用 GMP 库进行大整数进制转换示例

#include <iostream>
#include <gmpxx.h>

std::string mpzToBinary(const mpz_class& number) {
    mpz_class temp = number;
    std::string binary;
    while (temp > 0) {
        binary = (temp % 2 == 0? "0" : "1") + binary;
        temp /= 2;
    }
    if (binary.empty()) {
        binary = "0";
    }
    return binary;
}

std::string mpzToOctal(const mpz_class& number) {
    mpz_class temp = number;
    std::string octal;
    while (temp > 0) {
        int remainder = temp % 8;
        octal = std::to_string(remainder) + octal;
        temp /= 8;
    }
    if (octal.empty()) {
        octal = "0";
    }
    return octal;
}

std::string mpzToHexadecimal(const mpz_class& number) {
    mpz_class temp = number;
    std::string hexadecimal;
    while (temp > 0) {
        int remainder = temp % 16;
        if (remainder < 10) {
            hexadecimal = std::to_string(remainder) + hexadecimal;
        } else {
            hexadecimal = static_cast<char>(remainder - 10 + 'A') + hexadecimal;
        }
        temp /= 16;
    }
    if (hexadecimal.empty()) {
        hexadecimal = "0";
    }
    return hexadecimal;
}

mpz_class binaryToMpz(const std::string& binary) {
    mpz_class number(0);
    for (char digit : binary) {
        number *= 2;
        if (digit == '1') {
            number += 1;
        }
    }
    return number;
}

mpz_class octalToMpz(const std::string& octal) {
    mpz_class number(0);
    for (char digit : octal) {
        number *= 8;
        number += digit - '0';
    }
    return number;
}

mpz_class hexadecimalToMpz(const std::string& hexadecimal) {
    mpz_class number(0);
    for (char digit : hexadecimal) {
        number *= 16;
        if (digit >= '0' && digit <= '9') {
            number += digit - '0';
        } else if (digit >= 'A' && digit <= 'F') {
            number += digit - 'A' + 10;
        } else if (digit >= 'a' && digit <= 'f') {
            number += digit - 'a' + 10;
        }
    }
    return number;
}

int main() {
    std::string largeBinary = "110110110110110110110110110110110110110110110110110110110110110110";
    mpz_class largeNumber = binaryToMpz(largeBinary);
    std::cout << "Binary " << largeBinary << " in octal is " << mpzToOctal(largeNumber) << std::endl;
    std::cout << "Binary " << largeBinary << " in hexadecimal is " << mpzToHexadecimal(largeNumber) << std::endl;

    std::string largeOctal = "123456712345671234567123456712345671234567123456712345671234567123";
    largeNumber = octalToMpz(largeOctal);
    std::cout << "Octal " << largeOctal << " in binary is " << mpzToBinary(largeNumber) << std::endl;
    std::cout << "Octal " << largeOctal << " in hexadecimal is " << mpzToHexadecimal(largeNumber) << std::endl;

    std::string largeHexadecimal = "ABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCDEF";
    largeNumber = hexadecimalToMpz(largeHexadecimal);
    std::cout << "Hexadecimal " << largeHexadecimal << " in binary is " << mpzToBinary(largeNumber) << std::endl;
    std::cout << "Hexadecimal " << largeHexadecimal << " in octal is " << mpzToOctal(largeNumber) << std::endl;

    return 0;
}

通过上述代码示例,可以看到使用 GMP 库能够方便地处理大整数的多进制转换,这在密码学、高精度计算等领域具有重要应用。

6. 进制转换在实际项目中的应用

6.1 密码学

在密码学中,进制转换常用于数据的加密和解密过程。例如,一些加密算法会将明文数据转换为二进制或十六进制形式进行处理,然后再将加密后的结果转换回合适的进制输出。

6.2 嵌入式系统

在嵌入式系统开发中,经常需要与硬件设备进行交互,而硬件设备可能使用特定进制的数据格式。例如,某些微控制器的寄存器配置可能使用二进制或十六进制来表示不同的功能选项。

6.3 网络编程

在网络编程中,数据在传输过程中可能需要进行进制转换。例如,IP 地址通常以点分十进制的形式表示,但在网络协议中可能需要转换为二进制或其他进制进行处理。

7. 总结

通过本文的详细介绍,我们深入了解了 C++ 中多进制间互转换的原理、实现方法以及在实际项目中的应用。无论是基础的十进制与二进制、八进制、十六进制之间的转换,还是借助 GMP 库进行大整数的进制转换,都为我们在不同编程场景下处理数据提供了有力的工具。掌握这些知识和技能,对于提升编程能力以及解决实际问题具有重要意义。希望读者通过学习本文,能够更加熟练地运用 C++ 进行多进制转换相关的编程工作。