一张支票上写错大写金额,银行直接退票;合同里大写和小写对不上,出争议时解释成本极高。大写金额之所以存在,是因为「壹仟零伍拾元叁角」这种写法笔画多、改一笔就露馅,而「1050.30」加一横就能变成「11050.30」。
先把结论摆在这:到元为止必须加「整」,有角分就不加「整」;中间连续几个 0 只读一个「零」;小写金额前写「¥」,大写金额前写「人民币」三个字。 下面按《正确填写票据和结算凭证的基本规定》的口径逐条拆开讲。
一、大写不是繁体字,是金融专用计数汉字
很多人以为「壹贰叁」是「一二三」的繁体字,其实不是。它们是一套专为防篡改设计的计数用汉字,正式名称叫「大写数字」:
- 「一」加一笔就变成「二」「十」「七」,「壹」改不了
- 「三」加一笔变「王」,「叁」改不了
- 「二」加一笔变「三」「工」,「贰」改不了
所以票据规范明确要求:金额数字一律用「壹、贰、叁、肆、伍、陆、柒、捌、玖、拾、佰、仟、万、亿」,不得自造简化字。把「贰万」写成「二万」、「叁仟」写成「三千」,在银行柜面就是不受理。
同理,「零」也是这套体系的一部分,它的作用是把数字位钉死,防止在中间插位数。「1050」写成「壹仟零伍拾」,中间的「零」占住了百位,谁也插不进去。
二、数字 → 汉字对照表
| 阿拉伯数字 | 大写 | 位 / 单位 | 大写 |
|---|---|---|---|
| 0 | 零 | 十 | 拾 |
| 1 | 壹 | 百 | 佰 |
| 2 | 贰 | 千 | 仟 |
| 3 | 叁 | 万(10⁴) | 万 |
| 4 | 肆 | 亿(10⁸) | 亿 |
| 5 | 伍 | 元 | 元 |
| 6 | 陆 | 角(0.1 元) | 角 |
| 7 | 柒 | 分(0.01 元) | 分 |
| 8 | 捌 | 到元为止的收尾字 | 整(或「正」) |
| 9 | 玖 | — | — |
四个高频提醒:
- 「十」必须写「拾」,「百」写「佰」,「千」写「仟」,带单人旁/人字头的那个,不是普通写法
- 「万」「亿」没有大写变形,就写本字
- 「二」在「万」「亿」位上仍然写「贰」:2 万 → 贰万元整,不是「二万元整」
- 「整」和「正」等价,票据上写哪个都行,但同一张票别混着用
三、「零」的四种情况,全错在这一步
这是最容易翻车的地方,一共就四条规则:
1. 数字中间有 0,写「零」
1409.50 → 壹仟肆佰零玖元伍角(百位的 0 要读出来)
2. 中间连续多个 0,只读一个「零」
10001 → 壹万零壹元整(不是「壹万零零壹元整」) 100000001 → 壹亿零壹元整
3. 万级 / 亿级后面接一个不足四位的小节,要补一个「零」
300600 → 叁拾万零陆佰元整(万级是「30」,元级「600」不足四位,补零) 30600 → 叁万零陆佰元整
即:高位已经写过内容,而本组数值小于 1000 时,先补一个「零」再读。这也是「壹万贰仟元整」(12000)不需要零、而「壹万零壹元整」(10001)需要零的原因——前者的元级是「2000」,四位数完整。
4. 末尾的 0 不读
12000 → 壹万贰仟元整(末尾三个 0 都不读,用「整」封口) 10000 → 壹万元整
角位为 0 而分位不为 0 时,也要补一个「零」:
100.05 → 壹佰元零伍分 16409.02 → 壹万陆仟肆佰零玖元零贰分
而元位为 0 时直接跳过:0.05 → 伍分,不写「零元伍分」;0.50 → 伍角。
四、「整 / 正」什么时候加
| 金额到哪一位 | 要不要加「整」 | 示例 |
|---|---|---|
| 到元为止(角分都是 0) | 必须加 | 10000 → 壹万元整 |
| 到角为止(分位是 0) | 可以不加 | 1050.30 → 壹仟零伍拾元叁角 |
| 有分 | 不加 | 1234.56 → 壹仟贰佰叁拾肆元伍角陆分 |
| 金额为 0 | 加 | 0 → 零元整 |
注意「到角为止」这一行的可以两个字:规范原文是「在『角』之后可以不写『整』(或『正』)字」。所以「壹仟零伍拾元叁角」和「壹仟零伍拾元叁角整」在实务中都收,但工具按不加输出——加了也不算错。
五、常见金额对照表(工具实跑结果)
下表每个数字都是直接调用转换函数跑出来的真值,照着抄不会错:
| 小写金额 | 大写金额 | 关键点 |
|---|---|---|
| 0.05 | 伍分 | 元位为 0 直接跳过,不写「零元」 |
| 0.50 | 伍角 | 到角为止,不加「整」 |
| 1.00 | 壹元整 | 到元为止,加「整」 |
| 100.05 | 壹佰元零伍分 | 角 0 分非 0,补一个「零」 |
| 1,234.56 | 壹仟贰佰叁拾肆元伍角陆分 | 有分不加「整」 |
| 6,003.07 | 陆仟零叁元零柒分 | 两处「零」:中间一个、角位一个 |
| 10,001.00 | 壹万零壹元整 | 连续 0 只读一个「零」 |
| 12,000.00 | 壹万贰仟元整 | 元级四位数完整,不补零 |
| 160,409.20 | 壹拾陆万零肆佰零玖元贰角 | 万级缺千位补零 + 十位补零 |
| 999,999,999,999.00 | 玖仟玖佰玖拾玖亿玖仟玖佰玖拾玖万玖仟玖佰玖拾玖元整 | 亿级极限写法 |
| 100,000,000.00 | 壹亿元整 | 整数级直接封「整」 |
| 100,000,001.00 | 壹亿零壹元整 | 亿与元之间补一个「零」 |
| −1,050.30 | 负壹仟零伍拾元叁角 | 负数以「负」字开头 |
两个边界:金额达到 1 兆元(10¹² 元)时工具会提示「金额过大,超出可处理范围」;输入超过两位小数按四舍五入到分处理,且内部先放大 100 倍再取整,规避了 9.995 这类浮点误差(结果是「壹拾元整」,而不是「玖元玖角玖分」)。
六、想自己实现?核心逻辑就这几行
如果要在自己的系统里生成大写,思路是「四位分组 + 组内补零 + 组间补零」:
const D = ['零','壹','贰','叁','肆','伍','陆','柒','捌','玖']
const U = ['仟','佰','拾','']
const S = ['', '万', '亿', '兆']
// 一个 0~9999 的四位组
function groupUpper(g) {
const d = [Math.floor(g / 1000), Math.floor(g / 100) % 10, Math.floor(g / 10) % 10, g % 10]
let s = '', zero = false
for (let i = 0; i < 4; i++) {
if (d[i] === 0) { zero = true; continue }
if (zero && s) s += '零' // 组内:前面有 0 且已写过数字 → 补零
zero = false
s += D[d[i]] + U[i]
}
return s
}
function intUpper(n) { // 非负整数 → 大写
const gs = []
for (let t = Math.floor(n); t > 0; t = Math.floor(t / 10000)) gs.push(t % 10000)
let out = ''
for (let i = gs.length - 1; i >= 0; i--) {
if (!gs[i]) continue
if (out && gs[i] < 1000) out += '零' // 组间:本组不足四位 → 补零
out += groupUpper(gs[i]) + S[i]
}
return out || '零'
}
function toRMBUpper(amount) {
const cents = Math.round(Number((amount * 100).toFixed(4))) // 规避浮点误差
const sign = cents < 0 ? '负' : ''
const a = Math.abs(cents)
const yuan = Math.floor(a / 100), jiao = Math.floor(a / 10) % 10, fen = a % 10
let s = yuan ? intUpper(yuan) + '元' : ''
if (!jiao && !fen) return sign + (yuan ? s : '零元') + '整' // 到元为止 → 整
if (jiao) s += D[jiao] + '角'
else if (yuan) s += '零' // 角 0 分非 0 → 补零
if (fen) s += D[fen] + '分'
return sign + s
}
两个坑值得单独说:
- 取整必须用
Math.floor,不能用| 0。| 0是 32 位按位运算,金额超过 2,147 万元(分位数超过 2³¹)就会溢出成乱码,999999999999会直接变成「零元整」 - 别用
toFixed(2)直接截分。(9.995).toFixed(2)得到"9.99"(二进制精度),财务口径应该是进位到 10.00。上面先× 100再toFixed(4)兜一层,结果就是「壹拾元整」
关键是两个「零」的位置:groupUpper 里管组内连续 0 压缩成一个,intUpper 里管组间补位零。分清楚这两处,剩下就是「整」字的加法。
七、高频翻车点
- ¥ 符号写不写:小写金额前要写「¥」(如 ¥1050.30),大写金额前不写 ¥,写「人民币」三个字,且三个字与数字之间不得留空白(留了就可能被插入数字)
- 「元」写成「圆」行不行:规范文本用「元」,实务中「圆」多数机构也受理,但同一张票据别混用,正式凭证统一用「元」
- 角分全为 0:一定要写「整」,写成「壹万元」是不完整的,容易被补字
- 要不要加「人民币」三字:票据、支票、正式合同建议加,写成「人民币壹万元整」;有些合同模板在标题里已写明币种,正文可省,但省了就有争议空间
- 手写 vs 打印:手写用黑色签字笔或碳素笔,写错不能涂改、不能描,只能作废重开;机打凭证的大写由系统带出,但开票前仍要核对大写与小写是否一致——系统带出的是按它自己的规则生成的,规则写错照样错
- 支票日期也要大写:票据出票日期必须用中文大写,且月为壹、贰、壹拾的,日为壹至玖、壹拾、贰拾、叁拾的,前面要加「零」(1 月 15 日 → 零壹月壹拾伍日);日为拾壹至拾玖的,前面加「壹」(10 月 19 日 → 零壹拾月壹拾玖日)。多数大写转换工具只管金额不管日期,日期那栏得自己按规则写
八、开票场景:先价税分离,再写大写
实际开票时,大写金额往往不是直接拿到的。客户报的「含税总价」要先做价税分离算出不含税金额和税额,票面上的金额栏、税额栏、价税合计栏各有各的数字,最后才轮到大写。
比如含税价 10,600 元、税率 6%:不含税价 = 10,600 ÷ 1.06 = 10,000 元,税额 = 600 元。三栏都要填,大写写的是「壹万元整」(对应不含税金额栏)或「壹万零陆佰元整」(对应价税合计栏),填哪一栏就得写哪一栏的数,混了就是废票。
税率不同结果差很多,手算容易在小数位上出错,先用工具跑一遍价税分离,再把结果转成大写,比一路手算稳:
九、开票 / 合同填写核对清单
- 大小写是否一致——逐位对一遍,这一步能挡掉九成错误
- 「整」字有没有加对——角分全 0 必加,有「分」绝不加
- 中间的「零」有没有漏——尤其是万级/亿级后面接不足四位的小节
- 有没有自造简化字——「二万」「三千」「十元」都是不合格写法
- 大写前有没有写「人民币」,且与数字之间不留空格
- 小写金额前有没有加「¥」
- 手写是否有涂改痕迹——有就重开,别抱侥幸
- 支票日期是否用大写,且「零」「壹」的补位规则用对
- 合同场景下:金额条款同时写大小写,并注明「大小写不一致时以大写为准」(或反过来,但必须写明)
- 开票场景:先做价税分离,确认金额栏 / 税额栏 / 价税合计栏三个数字各归各位,再写大写
全部纯前端实现,输入的金额不上传服务器。