简介:数字进制转换器是一款面向计算机初学者、编程学习者及高校相关专业的实用工具,支持十进制、二进制、八进制和十六进制之间的双向转换,并涵盖除取余、权重累加等核心算法讲解,有助于理解数据在计算机中的表示方式,可帮助理解位操作、内存寻址等底层概念。资源包为zip压缩包,共二十二个文件,核心包括六个C#源码文件、三个可执行程序,以及解决方案与项目工程文件、界面资源文件、调试符号信息与说明文档等,整体仅四十五千字节,结构轻量且便于下载学习。已有三百一十二人学习下载,既能直接运行程序完成进制转换,也可通过源码研究Windows窗体界面设计与进制算法实现,是巩固计算机基础、备战课程设计或日常开发的好帮手。
1. 数字进制转换器:一次数据错位事故让我重新审视它
前阵子我排查一台设备的上行数据流,发现解析出来的温度值整体对不上,折腾了半天才定位到根因:协议文档里标注的是十六进制字符串,而上游代码直接按十进制去int()了。那一瞬间我意识到,数字进制转换器这种“基础得不能再基础”的工具,恰恰是协议对接、配置解析、底层调试里最容易翻车的一环。进制转换的本质,是一套在任意计数制之间安全搬运数值的表达层工具,它解决的不只是“算对结果”,更是“在什么场景下用哪条转换路径才不出偏差”。这篇文章要讲的是:把进制转换器从教科书公式变成能嵌入开发流程的工具——原理、实现、扩展和踩坑记录。适合需要写协议解析、嵌入式调试、数据处理和教学演示的开发者,也适合打算把进制转换沉淀成稳定基础库的团队参考。
2. 进制转换的两种数学模型与选型边界:位权展开和除基取余
2.1 位权展开:从任意进制回十进制的公式推演
进制转换的第一种模型是位权展开。一个 n 位的 B 进制数,其数值等于每一位数字乘以该进制基数的幂次之和。把这个展开式写清楚,回十进制的转换就只剩下求和,不需要任何迭代。为什么先讲这个公式?因为它能解释转换器里很多边界行为:二进制第 0 位权重是 1、第 1 位是 2、第 8 位是 256,因为权重是 2 的幂;十六进制字符串长度直接决定它可表示的最大值;小数部分的权重是负指数,看到一位小数就联想到 B 的负一次方,这是理解小数转换精度的起点。
我一般会在写代码前先把位权展开在纸上推一遍,因为一旦明确“从低位开始、逐位累乘 base”这条规则,后面的代码就只是它的迭代写法。很多实现里出现错误,都是因为从最高位开始算权重、却忘了在循环里乘 base,导致结果差了好几个数量级。下面这段是位权展开在 Python 里最直接的验证代码:
digits = "2A3F" # 以十六进制为例 base = 16 value = sum( int(ch, 16) * (base ** i) # 每一位数字乘上对应权重 for i, ch in enumerate(reversed(digits)) # 从最低位开始遍历 ) print(value) # 输出 10815这段代码的逻辑比较直观:reversed(digits)让字符串从最低位开始遍历,第 0 位代表 base 的 0 次方;int(ch, 16)把单个十六进制字符解释成 0 到 15 的数值;累加后得到 10815。它在功能上等价于 Python 内置的int("2A3F", 16),但我保留它的意义是可以看清楚每一位的权重分配,方便排查“结果差一位”这类问题。
这里要提一个参数边界:int(x, base)的 base 只允许 0 或 2 到 36。当 base 为 0 时,Python 按整数字面量规则自动推断 2、8、10、16 进制,但它对格式要求很严格,010这类带前导零的裸数字会直接抛错,这点在后面的避坑章会专门展开。
2.2 除基取余:十进制到任意进制的迭代过程
反方向,即从十进制到任意进制,核心模型是除基取余:把十进制数不断除以目标进制基数,记录每次的余数,余数倒序排列就是结果。这个迭代过程对负数和零要作特殊处理——零直接返回"0";负数可以先取绝对值转换再补符号位,也可以直接依赖取模运算的符号规则,但后者容易踩坑。
为什么不直接用查表法?查表只适用于固定进制、固定位数,比如把 0 到 255 的二进制表硬编码进代码,它能跑,但完全谈不上通用。除基取余对任意 base 都有效,时间复杂度是 O(log_b n),在工程里几乎可以忽略。落地时我会刻意把“除”实现为整数除法,而不是浮点数除法,因为浮点数在数值接近上限时会丢精度,影响后几位的余数。
下面这是十进制转二进制的最小实现,顺手还支持了 36 进制以内的任意目标进制:
def decimal_to_base(n: int, base: int) -> str: if n == 0: return "0" digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" result = [] sign = "-" if n < 0 else "" n = abs(n) while n > 0: n, remainder = divmod(n, base) # 整数除法和余数同步求出 result.append(digits[remainder]) return sign + "".join(reversed(result)) print(decimal_to_base(255, 16)) # FF print(decimal_to_base(-255, 2)) # -11111111这里的核心是divmod(n, base),它同时返回整除结果和余数,避免先//再%两次计算。余数作为digits字符串的索引,天然支持到 36 进制。reversed(result)必不可少——第一次得到的余数其实是最后一位,必须倒序才能拼成正确的进制字符串。这个实现的边界是 base 只能在 2 到 36 之间,base 超过 36 时需要替换字符集,那部分放在第 4 章讲。
2.3 选型理由:什么时候该手写,什么时候直接用内置函数
很多开发者会问:Python 明明有bin()、oct()、hex()和int(x, base),为什么还要自己写一个进制转换器?我的回答是:内置函数解决的是标准场景,它解决不了三个问题——统一接口、自定义字符集、批量数据处理。
具体来说,内置函数各自独立,bin()只输出二进制且带0b前缀,hex()带0x前缀。如果你要写一个支持多进制的模块,总得包一层才能对外提供一致接口。其次,当业务需要 36 进制以上或自定义字符集时,比如做短链接、邀请码、兑换码,内置函数完全帮不上忙。第三,内置的int("...", base)对非法字符的报错信息不够友好,比如它只会抛ValueError,但在批量解析上千行数据时,你需要知道是哪一行、哪个字符触发的错误。
我一般遵循这样的选型原则:临时转换用内置函数,产品功能用自封装模块。做成模块后,可以在入口统一做入参校验、统一处理大小写和前后缀,将来遇到大数或自定义字符集需求,只改一处实现,而不是在每个调用点打补丁。简单说,int(x, base)和bin()是一把好用的螺丝刀,但你要交付的是一整套工具架。
3. 用Python实现核心转换模块:从2到36进制的一体化接口
3.1 整数转换函数:整除取余法的落地与代码
这一节把上一章的理论落地成一个可以直接复制进项目的模块。我不只写一个bin的封装,而是做成双向通用接口:base_to_decimal把任意进制字符串转十进制,decimal_to_base把十进制转任意进制,两个函数共享同一个字符集常量,保证底层逻辑单一可信。
DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" def base_to_decimal(value: str, base: int) -> int: """把任意进制字符串转为十进制整数,base 必须在 2~36 之间。""" if not (2 <= base <= len(DIGITS)): raise ValueError(f"base 必须在 2 到 {len(DIGITS)} 之间,当前为 {base}") value = value.strip().upper() if value.startswith("+"): value = value[1:] sign = -1 if value.startswith("-") else 1 if value.startswith("-"): value = value[1:] result = 0 for ch in value: digit = DIGITS.find(ch) if digit == -1 or digit >= base: raise ValueError(f"字符 {ch} 不是合法的 {base} 进制数字") result = result * base + digit return sign * result def decimal_to_base(n: int, base: int) -> str: """把十进制整数转为任意进制字符串,兼容负数。""" if not (2 <= base <= len(DIGITS)): raise ValueError(f"base 必须在 2 到 {len(DIGITS)} 之间,当前为 {base}") if n == 0: return "0" sign = "-" if n < 0 else "" n = abs(n) parts = [] while n > 0: n, remainder = divmod(n, base) parts.append(DIGITS[remainder]) return sign + "".join(reversed(parts))逻辑说明:base_to_decimal用的是位权展开的迭代写法,每读到一个字符,把当前结果乘 base 再加新位,避免了单独计算幂,效率更高。校验发生在两个层面:一是 base 的范围校验,二是单个字符是否合法。decimal_to_base是除基取余的完整实现,负号单独拎出来处理,而不是让divmod在负数上做运算——负数取模的符号行为太容易出错了,这点在第 5 章会详细展开。
参数说明:value.strip().upper()让用户输入" 2a3f "或"2A3F"都能正确解析十六进制;统一转大写是为了和DIGITS常量中的大写字母对齐。if not (2 <= base <= len(DIGITS))这一行是很关键的边界控制,它同时挡住了 base=1、base=0、base=37 这些非法输入,避免后面循环逻辑出现死循环或索引越界。
3.2 参数细节:大小写、前缀和异常入参
转换函数只是第一步,真正让它能进生产的是入口参数的处理。先看大小写:十六进制里A和a语义相同,但如果不做统一,DIGITS.find(ch)会漏掉小写字母,报出误导性的ValueError,用户会以为是自己输入错了,实际是模块不支持小写。
再看前缀:用户经常把0x、0o、0b直接粘贴进来。我的做法是提供两个选项——要么在转换前自动剥掉已知前缀,要么要求调用方保证传入纯数字字符串。更实用的方式是在模块里加一个规范函数,统一负责剥前缀:
PREFIX_MAP = {"0B": 2, "0O": 8, "0X": 16} def normalize(value: str, base: int): """去掉进制前缀或空白,返回可解析的纯数字字符串。""" value = value.strip().upper() sign = "" if value.startswith(("+", "-")): sign, value = value[0], value[1:] if value[:2] in PREFIX_MAP and PREFIX_MAP[value[:2]] == base: value = value[2:] return sign + value这里的设计思路是:只有当前缀声明的进制和传入的 base 一致时才剥掉前缀,否则保留前缀,把错误留给后续的字符校验去处理。这样用户可以传0x2A+ base=16,也可以传2A+ base=16,结果一致;但如果用户传0b101却声明 base=16,就会在字符校验阶段报错,避免把二进制字符串误读成十六进制而得到完全不同的数值。
还有一个容易忽视的参数是允许的字符集长度。DIGITS只有 36 个字符,base 上限就是 36;如果业务需要 62 进制,即数字加大小写字母,就必须替换常量并重新审视base_to_decimal的校验逻辑。这种扩展我会放到第 4 章讲,这里只提醒一个设计原则:字符集常量必须是整个模块的唯一事实来源,任何函数都不要自己硬编码一份字符表。
3.3 命令行入口:让转换器能直接在终端里用
模块写好了,如果只能通过 Python 脚本调用,实用价值会打折扣。我通常会给这种小工具加一个命令行入口,让它能像bin、hex一样直接在终端里运行,也能在 shell 脚本里组合调用,成为临时排查数据的得力助手。
import sys def main(): args = sys.argv[1:] if len(args) < 3 or args[0] not in ("to", "from"): print("用法: 转换器.py to <进制> <十进制数> # 十进制转任意进制") print(" 转换器.py from <进制> <数字字符串> # 任意进制转十进制") return direction, base_str, number = args[0], int(args[1]), args[2] if direction == "to": print(decimal_to_base(int(number), base_str)) else: print(base_to_decimal(number, base_str)) if __name__ == "__main__": main()逻辑说明:命令行参数按“方向 + 进制 + 数值”三个位置解析,to表示十进制转目标进制,from表示把某进制字符串解析成十进制。sys.argv[1:]拿到的都是字符串,所以在调用转换函数前要显式转型。
参数说明:这个入口把“进制”独立放在第二个位置,是因为最常见的场景是“把一个数批量转到同一种进制”,独立出来便于脚本循环复用。如果你要在一个脚本里交替转换多种进制,也可以改成--base 16风格的参数,但那样会增加解析代码量。这里刻意不引入argparse,是想保持单文件工具零依赖;项目规模变大后,再迁移到argparse或click以获得自动帮助信息和参数校验。
4. 把转换器做得更贴近生产:负数、小数、自定义字符集与超大数
4.1 负数处理:符号位与补码的取舍
前面两个核心函数都按“符号 + 绝对值的字符串”方式处理负数,在很多业务场景里足够。但如果你对接的是底层协议,会遇到“负数到底该用符号位还是补码”的抉择。符号位表示法简单直观,适合人读;补码则更适合二进制层面的位运算和硬件协议。
我一般这样处理:基础转换模块永远输出符号位形式,也就是-1010,而补码转换做成单独函数,只在需要时调用。补码的算法是将负数取绝对值,按目标位宽取反再加 1,然后转成对应进制字符串。这里最关键的是“位宽”参数——同样是 -1,用 4 位二进制表示是1111,用 8 位是11111111,缺了位宽参数,补码转换就没有意义。
def to_twos_complement(n: int, width: int) -> str: """返回 n 在 width 位下的补码二进制字符串。""" if n >= 0: return format(n, f"0{width}b") mask = (1 << width) - 1 return format((abs(n) ^ mask) + 1, f"0{width}b") print(to_twos_complement(-1, 4)) # 1111 print(to_twos_complement(-8, 4)) # 1000参数说明:f"0{width}b"是 Python 格式化语法,0{width}b表示输出至少 width 位二进制并补零。mask = (1 << width) - 1生成 width 位全 1 掩码,abs(n) ^ mask是按位取反,再加 1 即补码。需要留意的是width必须能容纳该负数,-8在 4 位里正好是1000,但-9在 4 位里会溢出,这种溢出不会报错,输出结果却不再代表-9的真实补码,所以调用方要对 width 做前置校验,输入前先用n >= -(1 << (width - 1))判断一下。
4.2 小数转换:乘基取整的精度与截断策略
小数部分转换的思路是“乘基取整”:把小数部分不断乘以目标进制,取出整数位作为目标进制的小数位,再去掉整数位继续迭代,直到小数部分为零或达到精度上限。这个思路和整数部分的“除基取余”正好对称。
坑在于浮点数的二进制表示并不精确。0.1 在二进制里是无限循环小数,做转换时若不做精度截断,会一直循环到无穷。所以小数转换函数必须提供precision参数,明确转换到多少位就停止。
def decimal_to_base_frac(frac: float, base: int, precision: int = 16) -> str: """把十进制小数部分转为指定进制的字符串,返回小数点后的部分。""" if not (0 < frac < 1): raise ValueError("frac 必须是 0 到 1 之间的小数") parts = [] for _ in range(precision): frac *= base digit = int(frac) parts.append(DIGITS[digit]) frac -= digit if frac == 0: break return "".join(parts) print("0." + decimal_to_base_frac(0.25, 2)) # 0.01 print("0." + decimal_to_base_frac(0.1, 2, 8)) # 0.00011001...逻辑说明:循环内部每次把小数乘 base,整数位就是这个位上的数字,取走之后用剩余小数继续迭代。precision是硬性终止条件,否则像 0.1 这样的无限循环小数会让程序一直算下去。代码里frac -= digit是关键,漏掉它就是死循环。
参数说明:precision默认给 16 位,是因为大部分业务场景下 16 位十进制小数足够用;精度设得过大反而会把浮点数本身携带的存储误差暴露出来,让人误以为是算法错了。如果业务需要真正的高精度,请参考 4.4 节把小数也改成字符串运算的版本,或者直接用decimal.Decimal作为输入,不要在 float 上反复乘基。
4.3 自定义字符集:突破36进制限制的扩展
36 进制的限制来自DIGITS常量只有 10 个数字和 26 个大写字母。实际业务里,邀请码、短链、兑换码这些场景常常需要 62 进制甚至更高,以便在更短字符串里塞入更大的数值。扩展方式很简单:替换DIGITS常量并去掉长度校验的上限。
BASE62_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" def decimal_to_base62(n: int) -> str: parts = [] while n > 0: n, remainder = divmod(n, 62) parts.append(BASE62_DIGITS[remainder]) return "".join(reversed(parts)) or "0"这段代码没有做 base 参数化,而是直接写死 62 进制,目的是展示自定义字符集的本质——只要字符集长度等于进制基数、且每个字符唯一,除基取余算法就能正常工作。字符集可以是任意顺序,但如果顺序和数字的数值顺序不对应,转换结果对整个系统就是“自定义规则”,需要保证消费端用同一套字符集解析,这一点在生成兑换码时尤其重要。
我建议把自定义字符集做成模块顶部的可配置常量,并写单元测试锁定“生成后能解析回原值”这一条不变量。字符集一改,全部转换结果都会变,如果测试只覆盖固定用例,很容易在改字符集后留下线上数据解析不了的隐患。
4.4 超大数场景:用字符串运算避免溢出
Python 的int理论上没有上限,所以“大数溢出”在 Python 里并不常见。但如果你要把这个工具移植到 JavaScript、Go,或者要兼容某个约束严格的运行环境,就必须考虑整数上限。更常见的场景是:输入本身是超长进制字符串,比如一段几百位的十六进制文本,直接用int()能转,但内存和时间不一定划算。
字符串运算的版本把整数的加减乘除全部用手工方式实现,这里不展开完整代码,只给出设计思路和关键片段——核心是从高位到低位逐位求商的长除法:
def long_division(dividend_digits, base, divisor): remainder = 0 quotient = [] for ch in dividend_digits: current = remainder * base + ch # 当前被除数 quotient.append(current // divisor) # 商的这一位 remainder = current % divisor return quotient, remainder这个函数模拟的是纸上做除法:每一位进来时,先把上一位的余数乘以 base 再加当前位,形成新的被除数,然后商一位、余一位地推进。这样就不存在“整个数先转成 int”这个步骤,内存占用只和字符串长度成正比。
这里的取舍是:Python 里int()内置算法是用 C 实现的,性能远高于纯 Python 循环,所以除非目标平台确实有整数上限,否则不建议用字符串运算替代内置整型。真正需要长除法的场景通常是自定义高进制和自定义字符集同时启用时,为了统一行为而全部改成字符串实现。
5. 进制转换器避坑实录:5个典型翻车现象与修复
5.1 小数转换精度损失:0.1 转二进制后一长串尾巴
现象:调用decimal_to_base_frac(0.1, 2, 16)得到一长串二进制结果,和手算的 0.1 的二进制表示对不上,末尾几位在不同机器上跑结果也有差异,团队成员互相核对谁也说服不了谁。
原因:0.1 在二进制里是无限循环小数,无论精度设多大,输出都只是某个截断位置下的近似值。而 Python 的 float 在内存里本身也是一个近似值,0.1的真实存储值并不是精确的 0.1,所以转换结果天然携带双重误差。
解决:小数转换必须明确“精度”和“截断策略”。我的习惯是把 precision 设为业务可接受的位数,并在输出文档里标注这是近似值,不要试图让输出看起来像精确结果。如果业务要求高精度,改用decimal.Decimal或分数Fraction作为输入,在字符串运算层面做乘基取整,不要反复在 float 上做乘法和减法。
5.2 负数取模的符号陷阱:迭代求余时得到意外结果
现象:某个版本的decimal_to_base没有对负数做abs()处理,直接用while n > 0循环,输入-7、base 2 时要么返回空字符串,要么结果里混进一个多余的负号。
原因:Python 的取模运算%对负数的结果符号跟随除数,-7 % 2的结果是1而不是-1,而整除-7 // 2的结果是-4。如果直接拿divmod(-7, 2)的结果去拼字符串,余数的意义和正数场景不一致,倒序后的符号位置就会错乱。
解决:负数统一先取绝对值,转换完成后单独拼接负号,这正是第 3 章核心实现里的做法。这里要特别提醒:不要在写完代码后偷懒删掉n = abs(n)那一行,否则就是给自己埋雷。测试用例里至少要包含-1、-8、-255这三个典型值。
5.3 内置int()吃前缀的差异:0x、0o、0b 混用导致解析错误
现象:用户从协议文档复制了一串0x1F,直接用int("0x1F", 16)报错;后来有人改成int("0x1F", 0)能跑通,就把 base=0 用在了所有解析入口,结果遇到没有前缀的"010"时直接抛ValueError,或者在不同运行环境里行为不一致。
原因:int(str, base)在 base 非 0 的情况下不接受0x前缀;base=0 时虽然允许前缀并自动推断进制,但它按 Python 整数字面量语法严格要求输入,010这类带前导零的裸数字不是合法字面量,会直接报错。把 base=0 当作“万能解析”使用,恰恰把这种限制带进了所有输入。
解决:在模块入口统一做规范化。第 3.2 节的normalize函数就是干这个的——先剥前缀再解析,保证无论用户带不带0x,最终走同一条解析路径。这条规则要写成测试用例固定下来,防止后续有人往入口加一个“看起来更聪明”的前缀判断逻辑,又把坑踩回去。
5.4 自定义字符集的重复字符:校验没做全,转换结果错得离谱
现象:把自定义字符集从网上复制来改用时,字符集里同时存在0和字母O,生成邀请码后,用户肉眼看着两个码相同,解析结果却完全不同;更糟的情况是某些字符在常见字体里几乎一样,业务人员根本分不清。
原因:字符集是双向转换的索引表,重复字符会让DIGITS.find(ch)永远命中第一次出现的位置,导致某些字符永远解析不出正确的数值,编码反向验证时得到完全不同的值。
解决:定义字符集后立刻做唯一性校验。len(set(custom_digits)) == len(custom_digits)是最便宜的一道防线,建议直接写在模块的初始化位置,启动即报错,省得线上排查。此外,字符集里不要同时出现0/O、1/l/I这类易混淆字符,这是做邀请码和兑换码时的基本礼仪。
5.5 边界参数:base=1、空字符串、进制超过字符集长度
现象:用户传了base=1,转换函数进不了 while 循环或陷入死循环;传空字符串时,函数返回0或报IndexError,但日志里看不到是哪个参数触发;base=37 时,DIGITS越界抛出晦涩的索引错误。
原因:base=1 在数学上不存在,但很多代码只校验了 base 下限,忘了同时校验它不能大于字符集长度;空字符串在base_to_decimal里循环体一次都不执行,返回默认的0,这个行为有时是合理的,有时却掩盖了调用方的参数错误。
解决:在函数入口统一做三段式校验:
if not isinstance(base, int) or not (2 <= base <= len(DIGITS)): raise ValueError(f"base 必须是 2~{len(DIGITS)} 之间的整数") if value == "": raise ValueError("输入字符串不能为空")这段校验把 base 的类型、上下限、空字符串三类问题一次性挡住,避免后面在循环体里出现索引越界或死循环。经验是:进制转换器的报错信息一定要带 base 和 value 的具体值,否则线上日志根本没法定位——你看到IndexError时完全不知道是哪个用户的哪次输入触发。
6. 给进制转换器加一个验证闭环:穷举回测与模块化复用
一个转换器写完能跑只是起点,能不能放心交给业务,取决于你有没有一套能快速验证“任意进制字符串转十进制再转回去”保持一致性的测试。我自己的习惯是做一个回测脚本:在 2 到 36 进制的范围内,随机生成一批十进制数,调用decimal_to_base转出去、再用base_to_decimal转回来,断言两者相等。这个回测每改一次字符集或核心算法就跑一遍,几秒钟就能覆盖几十万组用例,比人工核对可靠得多。
import random for base in range(2, 37): for _ in range(2000): n = random.randint(-100000, 100000) encoded = decimal_to_base(n, base) decoded = base_to_decimal(encoded, base) assert n == decoded, f"base={base}, n={n}, got {encoded}"这个回测脚本看起来简单,但它能捕捉两类回归:一是字符集顺序改动导致的转换错位,二是大小写规范化逻辑被破坏导致解析失败。我把它放进提交前的检查流程,格式化代码后跑一遍再提交,后面几乎没有因为进制转换出过线上事故。回测通过只是一个必要不充分条件,像补码溢出这类问题还需要专门的位宽用例去覆盖。
模块化复用也是我反复强调的一件事。如果项目里已经有一个稳定的进制转换模块,后续遇到协议解析、短链生成、邀请码发放,直接导入并测试,而不是每处复制一份带不同 bug 的实现。我的习惯是把进制转换器做成独立模块,对外只暴露decimal_to_base、base_to_decimal、decimal_to_base_frac和回测函数,把字符集、精度、前缀处理作为模块配置,而不是散落在调用方。
最后一件事是给调用方提供一个明确约束:用户粘贴1010,他可能说是二进制,也可能是十进制,这个歧义无法靠代码消除。我能做的是强制要求调用时传入 base,而不是自作聪明去猜。一个工具如果存在不确定的输入分支,它就不适合被自动化流程调用,否则某天脚本误判进制,整个数据链路都会跟着偏。
这个模块在我的个人工具箱里已经迭代过三个版本,从最初只有两个函数,到后来补上小数、补码和回测。最大的教训是:任何一次对字符集或精度逻辑的改动,都必须同步改测试,否则两个月后你会对着一个看似正常、转换结果却偏离预期的函数无从下手。现在每次改完,我只记得把回测跑一遍,这个动作救了我好几次。希望这些实现思路与避坑记录能帮到你,让你在自己写进制转换器或接入现成方案时,少走几步弯路。
本文还有配套的精品资源,点击获取