超过机器整数范围时,仍然可以逐位计算
业务标识有时以很长的十进制文本存在。如果先强制转成机器整数,再进行其他进制转换,可能在真正处理之前就失去原值。
可以先检查每一位是否属于允许的数字,再以文本或逐位缓冲保存数值。这样后续计算处理的是数字序列,容量边界由输入上限和可用资源决定,而不是某个整数类型的位数。
进制转换并不要求把整个输入先放进一个固定宽度整数。对于很长的十进制文本,可以把它看成一列数字,用小范围的余数和商逐位处理。每个中间步骤只涉及一位数字与有限余数,因此不会随着整串数字变长而立刻溢出。
这条路线特别适合需要保留任意长度整数的编码工具。它解决的是整数表达转换,不处理小数点、负号或科学计数法;这些内容应在进入核心转换之前被明确拒绝或单独拆分。
一次长除法,产生一个商和一个余数
digits = "12345678901234567890"
base, remainder, quotient = 16, 0, []
for char in digits:
value = remainder * 10 + (ord(char) - ord("0"))
quotient.append(str(value // base))
remainder = value % base
q = "".join(quotient).lstrip("0") or "0"
assert q == "771604931327160493"
assert remainder == 2在这个片段里,remainder 始终小于目标基数。每次引入一个十进制数字,value 的范围也就有明确上限。注意它假设输入已经逐字符校验为 ASCII 数字,不能把减去字符零的操作当成校验本身。
商的每一位仍然按十进制组织。开头的零可以统一去除,但结果为零时需要保留一个零,避免用空字符串同时表示零值和没有结果。
重复取余,最后反转输出顺序
一条直接的路线是逐位计算商与余数,反复缩短尚未处理的数值。每轮产生的输出片段先暂存,完成后再按目标顺序组织起来。
零值与前导零应提前统一,否则它们可能在循环终止、输出长度和往返验证中产生特殊分支。中间结果的容量也需要单独检查,不能只限制最终输出。
alphabet = "0123456789ABCDEF"
number = 12345678901234567890
parts = []
while number:
number, remainder = divmod(number, 16)
parts.append(alphabet[remainder])
encoded = "".join(reversed(parts)) or "0"
assert encoded == "AB54A98CEB1F0AD2"字符串算法中,每次用上一轮得到的商继续长除法,余数映射成目标字符。余数从最低位开始出现,所以完成后需要反转。上面的 Python 示例依靠内置大整数提供参考结果,便于核对原生逐位算法是否正确。
目标字符表必须固定且没有重复字符。对于包含大小写字母的高进制编码,大小写通常具有不同数值,不能在输入阶段随意全部转小写。字符表顺序变化也会改变所有结果,需要作为格式版本的一部分维护。
反方向使用乘基数、加下一位
从目标进制恢复十进制时,可以按高位到低位扫描。每读一位,将当前十进制数字串乘以基数,再加上该位的数值。乘法和加法都通过十进制列上的进位完成,不需要固定宽度的完整整数容器。
这两种方向的算法都可能随着输入长度增长而增加明显的计算量。不限数值范围不等于不限输入规模,应该限制最大字符数,并在构造输出前考虑最坏长度。对频繁处理极大整数的场景,再评估成熟的大整数库是否更合适。
| 输入 | 建议约定 |
|---|---|
| 零 | 统一输出一个零字符 |
| 前导零 | 保留含义但规范化表达 |
| 负号 | 由外层解析,核心只处理绝对值 |
| 未知字符 | 拒绝整份输入 |
| 过长数字 | 在计算前拒绝 |
不要在出现非法字符时只返回已经转换的前缀。这样的结果看起来像一个正常整数,调用方很难意识到尾部从未参与计算。失败应与零值清楚区分。
用双向性质验证超长输入
更高进制可以让数字标识更紧凑,但只是改变表示方式。它并不会自然隐藏业务规模,也不替代访问权限检查。
验收时可以从零、小值和带前导零的输入开始,再加入超过常见整数范围的文本。将结果解码回规范化后的原值,比仅检查输出包含哪些字符更能说明转换正确。
除了已知答案,还可以检查:规范化后的十进制输入,编码再解码应恢复原值;目标进制文本解码再编码,应得到约定的规范形式。前导零被主动去除时,需要比较数值含义而不是原始字节完全一致。
测试长度应跨过机器整数上限,而不只选很小的数字。再加入全为最大数字的输入,观察连续进位是否正确;加入只有一个零和大量前导零的输入,检查循环是否能够正常结束。
如果业务只是缩短普通范围内的编号,固定宽度整数实现通常更简单。只有当输入范围确实超过它,或协议明确允许长整数时,才需要承担逐位大数处理的额外成本。