財務系統、電子發票、合同生成等場景中,將阿拉伯數字金額轉換為人民幣大寫(如 13126.8 → 壹萬叁仟壹佰貳拾陸元捌角整)是剛需。本文提供一個健壯、可配置的 rmb_format() 函數,處理了四捨五入、連續零合併、整數部分結尾零補充、角分單位、幣種單位(元/圓)等細節問題。
<?php
/**
* 人民幣小寫轉大寫
* @param string $number 金額數值(字符串或數字)
* @param string $int_unit 幣種單位,默認"元",可選"圓"
* @param bool $is_round 小數部分是否四捨五入,默認 true
* @param bool $is_extra_zero 整數部分以0結尾且有小數時,是否在元後補"零",默認 false
* @return string 大寫金額字符串
*/
function rmb_format($money = 0, $int_unit = '元', $is_round = true, $is_extra_zero = false) {
// 1. 分割整數、小數部分
$parts = explode('.', (string)$money, 2);
$int = isset($parts[0]) ? strval($parts[0]) : '0';
$dec = isset($parts[1]) ? strval($parts[1]) : '';
// 2. 小數位數處理:超過2位按配置四捨五入或截斷
$dec_len = strlen($dec);
if ($dec_len > 2) {
$dec = $is_round
? substr(strrchr(strval(round(floatval("0." . $dec), 2)), '.'), 1)
: substr($parts[1], 0, 2);
}
// 3. 全零特判
if (empty($int) && empty($dec)) return '零';
// 4. 定義大寫數字、單位
$chs = array('零', '壹', '貳', '叁', '肆', '伍', '陸', '柒', '捌', '玖');
$uni = array('', '拾', '佰', '仟'); // 節內單位
$dec_uni = array('角', '分'); // 小數單位
$exp = array('', '萬'); // 節權位(萬、億...動態擴展)
$res = '';
// 5. 整數部分:從右向左,每4位一節處理
for ($i = strlen($int) - 1, $k = 0; $i >= 0; $k++) {
$str = '';
// 處理當前節內4位
for ($j = 0; $j < 4 && $i >= 0; $j++, $i--) {
$u = $int[$i] > 0 ? $uni[$j] : ''; // 非零才加單位
$str = $chs[$int[$i]] . $u . $str;
}
$str = rtrim($str, '零'); // 去除節末尾的零
$str = preg_replace('/零+/', '零', $str); // 合併連續零
// 動態構建節權位:萬、億、兆...
if (!isset($exp[$k])) {
$exp[$k] = $exp[$k - 2] . '億'; // 簡易擴展:萬→億→兆...
}
$u2 = $str !== '' ? $exp[$k] : '';
$res = $str . $u2 . $res;
}
// 6. 小數部分處理
$dec = rtrim($dec, '0'); // 去除小數末尾無意義零
if (!empty($dec)) {
$res .= $int_unit; // 加上"元/圓"
// 可選:整數尾數為0且有小數時補"零"(如 1960.30 → ...元零叁角)
if ($is_extra_zero && substr($int, -1) === '0') {
$res .= '零';
}
for ($i = 0, $cnt = strlen($dec); $i < $cnt; $i++) {
$u = $dec[$i] > 0 ? $dec_uni[$i] : '';
$res .= $chs[$dec[$i]] . $u;
// 僅有角無分時補"整"
if ($cnt == 1) $res .= '整';
}
$res = rtrim($res, '零');
$res = preg_replace('/零+/', '零', $res);
} else {
// 無小數部分
$res .= $int_unit . '整';
}
return $res;
}
?>
<?php
// 基本用法
echo rmb_format(13598.3); // 壹萬叁仟伍佰玖拾捌元叁角整
echo rmb_format(13126.8); // 壹萬叁仟壹佰貳拾陸元捌角整
echo rmb_format(1000); // 壹仟元整
echo rmb_format(0.01); // 零元壹分整
echo rmb_format(1960.30); // 壹仟玖佰陸拾元叁角整
// 使用"圓"作為單位
echo rmb_format(123.45, '圓'); // 壹佰貳拾叁圓肆角伍分整
// 關閉四捨五入(直接截斷)
echo rmb_format(123.456, '元', false); // 壹佰貳拾叁元肆角伍分整 (截斷)
// 開啟整數尾零補充
echo rmb_format(1960.30, '元', true, true); // 壹仟玖佰陸拾元零叁角整
?>
中文讀數習慣以 4 位為一節(個、萬、億、兆...)。代碼從右向左每 4 位切片,節內使用 拾/佰/仟,節間使用 萬/億。
rtrim($str, '零') 移除。preg_replace('/零+/', '零', $str) 合併為單個「零」。$u2 判斷)。$dec_uni。$is_extra_zero 控制「1960.30」是讀「...元叁角」還是「...元零叁角」,滿足不同財務規範。代碼中 $exp[$k] = $exp[$k-2] . '億' 簡易實現了萬→億→兆的擴展,支持超大數字(超過兆需進一步完善)。
| 問題 | 原因 | 解決 |
|---|---|---|
| 0.58 * 100 精度丟失 | 浮點數運算 | 傳入字符串 "13598.3" 或使用 BCMath 計算後再傳入 |
| 輸出「零元零角整」 | 未處理全零特判 | 函數開頭已處理 empty($int) && empty($dec) |
| 大數字超出範圍 | PHP 整數溢出 | 務必傳入字符串類型金額,函數內部按字符串處理 |
| 財務要求「圓」而非「元」 | 單位差異 | 參數 $int_unit = '圓' |
is_negative 判斷,輸出前綴「負」或「欠」。$chs、$uni 為多語言配置。本 rmb_format() 函數代碼緊湊、邏輯清晰、參數靈活,已處理財務場景中常見的零處理、四捨五入、單位差異等細節問題。直接複製即可用於生產環境,建議配合單元測試驗證核心用例後上線。