进制转换是编程中常见的需求,C++作为强类型语言,在处理数值计算和格式化输出时有丰富的标准库支持,我们可以基于这些特性快速实现一个通用的进制转换工具,同时保证计算过程的准确性和输出格式的灵活性。

进制转换的核心原理
不同进制之间的转换核心是基于位权的计算,比如二进制转十进制是将每一位的数值乘以2的对应次方后求和,十进制转其他进制则是通过不断取余和整除目标进制的基数实现。在C++中,我们可以直接利用整数运算的特性完成这些计算,不需要手动实现复杂的位运算逻辑。
支持的进制范围
本文实现的工具支持2到36进制的互相转换,因为超过36进制后没有足够的字符表示每一位数值,这个范围已经能满足绝大多数开发场景的需求。
基础进制转换实现
首先实现十进制转任意进制的函数,核心逻辑是不断对目标进制取余,将余数转换为对应字符,最后反转字符串得到结果。
#include <iostream>
#include <string>
#include <algorithm>
#include <stdexcept>
// 十进制转任意进制(2-36)
std::string decimal_to_base(long long num, int base) {
// 校验进制合法性
if (base < 2 || base > 36) {
throw std::invalid_argument("进制必须在2到36之间");
}
// 处理0的情况
if (num == 0) {
return "0";
}
bool is_negative = false;
if (num < 0) {
is_negative = true;
num = -num;
}
std::string result;
// 字符映射表,0-9对应0-9,10-35对应A-Z
const std::string digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
while (num > 0) {
int remainder = num % base;
result += digits[remainder];
num /= base;
}
if (is_negative) {
result += '-';
}
// 反转字符串得到正确顺序
std::reverse(result.begin(), result.end());
return result;
}
接下来实现任意进制转十进制的函数,遍历输入字符串的每一位,计算对应的位权值累加即可。
// 任意进制转十进制(2-36)
long long base_to_decimal(const std::string& num_str, int base) {
if (base < 2 || base > 36) {
throw std::invalid_argument("进制必须在2到36之间");
}
long long result = 0;
const std::string digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
bool is_negative = false;
size_t start_idx = 0;
if (num_str[0] == '-') {
is_negative = true;
start_idx = 1;
}
for (size_t i = start_idx; i < num_str.size(); ++i) {
char c = toupper(num_str[i]);
// 查找字符对应的数值
size_t pos = digits.find(c);
if (pos == std::string::npos || pos >= (size_t)base) {
throw std::invalid_argument("输入字符串包含非法字符");
}
result = result * base + pos;
// 检查溢出
if (result < 0) {
throw std::overflow_error("数值超出long long范围");
}
}
return is_negative ? -result : result;
}
数值计算的优化点
在进制转换的数值计算过程中,容易出现两个常见问题:溢出和非法输入,我们需要针对性做优化。
溢出处理
使用long long类型存储数值时,如果转换的数值过大,会出现溢出导致结果错误。上面的代码中我们在计算过程中判断结果是否小于0,因为正数溢出后会变成负数,以此抛出异常提示用户。
非法输入校验
输入字符串可能包含当前进制不支持的字符,比如二进制输入中包含2-9的字符,我们在转换前先校验每一位字符的合法性,避免计算出错。
格式化输出优化
默认的转换结果可能不符合用户的输出需求,比如希望十六进制结果小写,或者希望输出时添加进制前缀,我们可以通过格式化函数扩展输出能力。
// 格式化输出结果,支持添加前缀、大小写控制
std::string format_output(const std::string& raw_result, int base, bool add_prefix, bool upper_case) {
std::string result = raw_result;
// 处理大小写
if (upper_case) {
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
} else {
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
}
// 添加前缀
if (add_prefix) {
switch (base) {
case 2:
result = "0b" + result;
break;
case 8:
result = "0" + result;
break;
case 16:
result = "0x" + result;
break;
default:
break;
}
}
return result;
}
完整工具示例
将上面的函数整合起来,实现一个简单的交互式进制转换工具:
int main() {
try {
std::string input_num;
int from_base, to_base;
std::cout << "请输入原始数值: ";
std::cin >> input_num;
std::cout << "请输入原始进制: ";
std::cin >> from_base;
std::cout << "请输入目标进制: ";
std::cin >> to_base;
// 先转十进制
long long decimal_val = base_to_decimal(input_num, from_base);
// 再转目标进制
std::string raw_result = decimal_to_base(decimal_val, to_base);
// 格式化输出,默认添加前缀,十六进制小写
std::string final_result = format_output(raw_result, to_base, true, to_base != 16);
std::cout << "转换结果: " << final_result << std::endl;
} catch (const std::exception& e) {
std::cerr << "转换失败: " << e.what() << std::endl;
}
return 0;
}
使用注意事项
- 输入负数时,工具会保留负号,转换后依然是负数
- 如果转换的数值超出
long long的范围,会抛出溢出异常,可根据需求替换为更大的整数类型 - 36进制以上的转换需要自定义更多字符映射,可根据业务需求扩展
digits字符串