负进制数转换算法实现与解析
问题背景
在常规的进制转换中,我们通常处理的是以正整数为基数的系统,例如二进制、八进制和十六进制。然而,当基数为负数时,情况变得特殊:每个数位依然由非负数码组成,但其权重是负底数的幂次。例如,在-2进制中,每一位的权值依次为 (-2)0, (-2)1, (-2)2... 即 1, -2, 4, -8... 因此,一个十进制整数可以表示为这些交替符号幂次的线性组合。
核心原理
对于任意负进制 -R(其中 R ≥ 2),任何整数 N 都能唯一地表示成如下形式:
N = ak×(-R)k + ak-1×(-R)k-1 + ... + a1×(-R)1 + a0×(-R)0
其中每一位系数 ai 满足 0 ≤ ai < R。这与正进制类似,但除法取余过程需调整,因为标准模运算对负数可能产生负余数,而我们需要始终保证余数非负。
关键处理:修正负余数
在使用负基数进行除法时,若当前被除数为负,直接取模可能导致余数为负,违反数码范围要求。为此,引入修正机制:
- 计算临时余数 t = n % base
- 若 t < 0,则执行 t -= base 和 n += base
这一操作等价于将商增加1,并使余数"回退"到合法区间 [0, |base|-1] 内。
编码映射规则
当基数绝对值大于10时,超过9的数码用大写字母表示:A 表示10,B 表示11,依此类推。可通过字符数组建立索引映射:
const char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
该设计支持最大至36进制(含负基)的数码表示。
算法实现
采用栈结构存储从低位到高位生成的数码序列,最终逆序输出即可得到正确结果。流程如下:
- 读入十进制数 n 与负基数 base
- 循环执行带修正的取模运算,将每位数码压入栈中
- 当 n 变为0时停止循环
- 依次弹出栈中元素并查表输出对应字符
- 按格式追加基数说明
完整代码实现
#include <iostream>
#include <stack>
using namespace std;
int main() {
int n, base;
cin >> n >> base;
cout << n << '=';
if (n == 0) {
cout << "0";
} else {
stack<int> result;
while (n != 0) {
int remainder = n % base;
n /= base;
if (remainder < 0) {
remainder -= base;
n += base;
}
result.push(remainder);
}
const char symbols[] = "0123456789ABCDEFGHIJK";
while (!result.empty()) {
cout << symbols[result.top()];
result.pop();
}
}
cout << "(base" << base << ')';
return 0;
}
样例验证
输入:-25000 -16 → 输出:-25000=7FB8(base-16)
解释:7×(-16)3 + F(15)×(-16)2 + B(11)×(-16)1 + 8×(-16)0 = -25000,符合预期。