同一个数量,在不同场合有完全不同的写法:程序里写成 0xFF,票据上写成「贰佰伍拾伍元整」,钟表盘上写成 IV。这些写法各有一套规则,规则记混了不是显示难看,而是直接算错、票据被退回。
一、位权:手算进制转换的唯一原理
任何进制转十进制都是同一个动作——把每一位乘上它的位权再相加。二进制的位权是 1、2、4、8、16……:
1011(2) = 1×8 + 0×4 + 1×2 + 1×1 = 11
十六进制的位权是 1、16、256、4096……,字母 A 到 F 代表 10 到 15:
2F(16) = 2×16 + 15 = 47
反方向是「除基取余、逆序读」:11 除 2 得商 5 余 1,5 除 2 得商 2 余 1,2 除 2 得商 1 余 0,1 除 2 得商 0 余 1,余数从后往前读就是 1011。
真正省时间的是分组换算。因为 16 = 2 ^ 4,十六进制一位正好对应二进制四位;因为 8 = 2 ^ 3,八进制一位对应二进制三位。所以十六进制与二进制之间根本不用经过十进制:0xC3 拆成 C = 1100、3 = 0011,直接拼成 11000011。反过来 10110101 从右往左四位一组,1011 = B、0101 = 5,就是 0xB5。八进制同理,三位一组。
进制转换器把二进制、八进制、十进制、十六进制以及三十二进制、六十四进制放在同一屏,在任意一个输入框里打字,其他框会自动同步,适合校对手算结果。它还带「生成随机数」按钮,练手算的时候可以拿它出题、用其他框对答案。
二、编程里的前缀与补码:工具能做什么、不能做什么
先说工具的实际边界,这直接决定你该怎么输入:
- 它只处理非负整数。 内部换算把十进制解析为整数,负数与小数不参与转换,十进制输入框会过滤掉负号和小数点。
- 它不识别
0x、0b、0o前缀。 每个输入框只保留自己进制的合法字符,其他字符被直接丢掉。 - 它的三十二进制与六十四进制是「数位换算」,不是编码。 工具把
A-Z2-7当作三十二进制的 32 个数位、把A-Za-z0-9+/当作六十四进制的 64 个数位,按位权算成一个整数。这与传输用的 Base64 编码完全是两件事——后者按每 3 字节切成 4 个字符,还带=补位。想做 Base64 编解码请用专门的编码工具,别用这里的数位换算。
前缀被过滤这件事有个隐蔽的坑。把 0xFF 粘进十六进制框,x 被过滤后剩 0FF,结果仍是 255,看起来没问题;但把 0b1010 粘进十六进制框,b 本身就是合法的十六进制数位,过滤后剩下 0b1010,会被当成 0xB1010 = 725008。所以规范做法只有一条:粘贴前自己把前缀删掉,并且确认粘进的是对应进制的框。
负数在计算机里用补码表示,这部分需要手算,工具不代劳。补码的规则是「取反加一」,或者更快的算法是 2 ^ 位宽 - |负数|。以 8 位为例,-5 就是 256 - 5 = 251,二进制 11111011。坑在于同一串二进制的含义取决于位宽与是否有符号:0x80 在 8 位无符号里是 128,在 8 位有符号里是 -128;0xFFFF 在 16 位无符号里是 65535,有符号则是 -1。进制转换器一律按无符号解释,所以你在里面输入 11111011 得到的是 251 而不是 -5,这不是算错,是口径不同。
另外要注意大数精度:JavaScript 的常规数字类型只能精确表示到 2 ^ 53 - 1,也就是 9007199254740991。超过这个量级的整数在浏览器端换算会丢精度,结果的末几位不可信,需要处理超大整数时应改用支持大整数的环境。
三、人民币大写:零、整、角分的规范写法
中文大写数字是 零壹贰叁肆伍陆柒捌玖,单位是 拾佰仟万亿。它存在的唯一理由是防篡改:小写的 1 加两笔就成 7,3 描一下能变 8,而 壹 改成 柒、叁 改成 捌 在字形上做不到。所以银行结算凭证、发票、合同的金额栏都要求大写,并且规定大小写金额必须一致,不一致的凭证银行不予受理。
规范写法里最容易错的是零和整这两处:
- 金额到「分」为止。 人民币最小单位是分,大写金额也只写到分,不写厘。
- 整数金额后必须写「整」或「正」。
1234元要写成「壹仟贰佰叁拾肆元整」,只写「壹仟贰佰叁拾肆元」不完整。 - 有分时不写「整」。
1234.56是「壹仟贰佰叁拾肆元伍角陆分」,后面不加「整」。 - 角位为零而分位不为零时,必须补写「零」。
1234.05的规范写法是「壹仟贰佰叁拾肆元零伍分」,漏掉这个「零」就给了篡改空间。 - 中间连续的零合并成一个「零」。
100005写「壹拾万零伍元整」,不写成一串零。 - 万、亿位要按节写。
10502000是「壹仟零伍拾万贰仟元整」,节与节之间的零不能省。
数字大小写转换一次给出五种结果:中文小写(一千二百三十四)、中文大写(壹仟贰佰叁拾肆)、金额大写(壹仟贰佰叁拾肆元整),以及逐位小写与逐位大写(一二三四、壹贰叁肆,适合电话号码、编号这类「读数字」而不是算数值的场景)。金额大写这一路完全按上面的规范走:整数金额补「整」,角位为零而分位不为零时自动补「零」,1234.05 输出的是「壹仟贰佰叁拾肆元零伍分」。反向也支持:把「壹仟贰佰叁拾肆」还原成 1234,带「元 / 角 / 分 / 整」或「人民币 / ¥」的写法会自动按金额口径解析,「壹佰元零伍分」得到 100.05。工具页附有完整的格式说明,填单前对一眼更稳。
四、罗马数字:加减法则、以及为什么没有零
罗马数字只有七个符号:I 是 1、V 是 5、X 是 10、L 是 50、C 是 100、D 是 500、M 是 1000。组合规则是加减并用:
- 大符号在前表示相加。
VI = 5 + 1 = 6,LX = 50 + 10 = 60。 - 小符号在前表示相减,且只允许六种组合。 只有
IV(4)、IX(9)、XL(40)、XC(90)、CD(400)、CM(900)。也就是说只有I、X、C可以充当减数,而且只能减紧邻的两个量级,IL表示49是错的,49要写XLIX。 - 相同符号最多连写三次。
III是3,4必须写IV。钟表盘上常见的IIII属于传统写法,不符合规范但含义明确。 V、L、D不能重复。10不写VV。
拆解一个稍复杂的例子:1994 = M + CM + XC + IV = 1000 + 900 + 90 + 4,写作 MCMXCIV;2026 则是 MM + XX + VI = MMXXVI。
这套体系有两个结构性的空缺:没有零,也没有负数。原因是它是记数符号的累加体系,而不是位值制——每个符号自带固定量值,靠拼接表达总量,既不需要一个「空位」符号,也没有表示「相反方向」的机制。这也是为什么罗马数字做不了竖式运算,历史上算账要靠算板辅助。另一个限制是上限:标准符号最大到 M,能表达的范围是 1 到 3999,更大的数要在字母上加横线表示乘一千,而横线在普通文本里没法打出来。
罗马数字转换覆盖的正是 1 到 3999。数字转罗马时它按 M、CM、D、CD、C、XC、L、XL、X、IX、V、IV、I 的顺序贪心拆解,并把每一步的构成展示出来,可以直接对照上面的例子核验;罗马转数字支持大小写混输,会逐字符解析、标出减法组合,还会判断输入是否规范——像 IIII 会被正确识别为 4,同时提示它不是规范写法。
五、常见误算原因
- 粘贴时带着
0x、0b前缀。 前缀字符可能被过滤,也可能刚好是合法数位而改变结果,务必自己删掉。 - 在二进制与十六进制之间绕道十进制。 四位一组直接拼是更快也更不容易错的做法。
- 用无符号换算去理解负数。 补码要先定位宽,
11111011在 8 位有符号下才是-5。 - 把六十四进制数位换算当成 Base64 编码。 两者算法不同,结果自然不一样。
- 金额大写漏写「整」或漏补「零」。 这两处是票据被退回的高频原因。
- 罗马数字用了不允许的减法组合。
IL、IC这类写法不合规,49是XLIX、99是XCIX。
常见问题
为什么我输入负数,进制转换器没有反应?
工具只处理非负整数,负号会被输入过滤掉。需要看负数的二进制表示,请先确定位宽,用 2 ^ 位宽 - |负数| 手算出补码对应的无符号值,再把这个值填进十进制框换算。
十六进制和二进制怎么快速互转?
按四位一组拆分:十六进制每一位展开成四位二进制,二进制从右往左每四位合成一个十六进制位。0xC3 对应 11000011,10110101 对应 0xB5。八进制则是三位一组。
大写金额里「零」到底什么时候要写?
中间有空位时要写,末尾不写。角位为零而分位不为零,必须写成「元零几分」,1234.05 是「壹仟贰佰叁拾肆元零伍分」;金额是整数则写「元整」;写到「角」为止的,「角」后面可以写「整」也可以不写。数字大小写转换的金额大写按这套规则自动处理,补「零」和补「整」都不用自己判断,写到「角」为止时不额外加「整」。
罗马数字能表示零吗?为什么工具只支持到 3999?
不能。罗马数字是符号累加体系而非位值制,没有表示空位的零,也没有负数。上限则是因为标准符号最大是 M(1000),超过 3999 需要在字母上加横线表示乘一千,普通文本无法表达,所以工具把范围限定在 1 到 3999。