首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 金額格式化函式:千分位分隔、小數位控制、貨幣符號完整實作

     編輯:PHP 綜合知識

金額顯示是電商、金融系統核心需求。本文提供功能完整的 `format_money()` 函式,支援負數處理、任意小數位、貨幣符號自訂、千分位分隔符自訂,並解析浮點數精度陷阱與現代 NumberFormatter 替代方案。

核心函式實作

<?php
/**
 * 格式化金額
 * @param float|int $money 金額 (分為單位建議乘 100 存整數)
 * @param int $decimals 小數位數 (預設 2)
 * @param string $symbol 貨幣符號 (預設 ¥)
 * @param string $thousandsSep 千分位分隔符 (預設 ,)
 * @param string $decimalPoint 小數點符號 (預設 .)
 * @return string 格式化後字串
*/
function format_money($money, $decimals = 2, $symbol = '¥', $thousandsSep = ',', $decimalPoint = '.') {
    // 1. 處理負號
    $negative = $money < 0;
    $money = abs($money);
    
    // 2. 使用 sprintf 精確控制小數 (避免浮點數精度問題)
    $format = '%01.' . (int)$decimals . 'f';
    $moneyStr = sprintf($format, $money);
    
    // 3. 分離整數與小數部分
    if ($decimals > 0) {
        [$intPart, $decPart] = explode('.', $moneyStr);
        $decimal = $decimalPoint . $decPart;
    } else {
        $intPart = $moneyStr;
        $decimal = '';
    }
    
    // 4. 整數部分加千分位分隔符 (從右向左每 3 位)
    $reversed = strrev($intPart);
    $chunks = str_split($reversed, 3);
    $formattedInt = strrev(implode($thousandsSep, $chunks));
    
    // 5. 組合結果
    return $symbol . ($negative ? '-' : '') . $formattedInt . $decimal;
}

// 使用範例
echo format_money(1234567.89);        // ¥1,234,567.89
echo format_money(-98765.5);          // -¥98,765.50
echo format_money(1000, 0, '$');      // $1,000
echo format_money(1234.5, 3, '€', ' ', ','); // €1 234,500
?>

浮點數精度陷阱與解決

<?php
// ❌ 錯誤:直接運算浮點數
$price = 19.99 * 3; // 59.970000000000006
echo format_money($price); // ¥59.97 (sprintf 自動四捨五入掩蓋問題)

// ✅ 正確:使用整數分為單位
$priceCents = 1999 * 3; // 5997
echo format_money($priceCents / 100); // ¥59.97

// ✅ 進階:BCMath 高精度
$price = bcmul('19.99', '3', 2); // '59.97'
echo format_money($price);

現代替代方案:NumberFormatter (intl 擴展)

<?php
// 需安裝 php-intl 擴展
$fmt = new NumberFormatter('zh_TW', NumberFormatter::CURRENCY);
$fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, 'NT$');

echo $fmt->formatCurrency(1234567.89, 'TWD'); // NT$1,234,568 (依地區四捨五入)
echo $fmt->formatCurrency(-98765.5, 'TWD');   // -NT$98,766

// 更多控制
$fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, 2);
$fmt->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 2);
echo $fmt->formatCurrency(1000, 'TWD'); // NT$1,000.00

資料庫儲存最佳實踐

策略優點缺點適用場景
DECIMAL(10,2)精確、SQL 可直接運算固定小數位、範圍有限一般金額、價格
BIGINT (分為單位)極高精度、效能最好、無圍誤差讀取需除 100、跨幣種複雜高頻交易、會計核心
VARCHAR (字串)無精度限制無法 SQL 運算、排序錯誤極大金額、多幣種混存

國際化 (i18n) 考量

  • 千分位分隔符:西方 `,`、歐洲 `.`、印度 `,` (但分組不同 1,00,000)
  • 小數點:西方 `.`、歐洲 `,`
  • 貨幣符號位置:前綴 `$100`、後綴 `100 €`、含空格 `100 €`
  • 負數格式:`-$100`、`-$100`、`($100)` (會計格式)
// 使用 NumberFormatter 自動處理地區差異
$locales = ['zh_TW', 'en_US', 'de_DE', 'fr_FR', 'ja_JP'];
foreach ($locales as $locale) {
    $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY);
    echo "$locale: " . $fmt->formatCurrency(1234.56, 'USD') . "\n";
}
// zh_TW: NT$1,234.56
// en_US: $1,234.56
// de_DE: 1.234,56 $
// fr_FR: 1 234,56 $
// ja_JP: ¥1,235 (日圓無小數)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved