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

Java 百分比計算最佳實務:DecimalFormat 與 BigDecimal 精準處理

     編輯:Java 相關知識

前言

百分比計算看似簡單,實際開發中常遇到整數除法截斷、浮點數精度誤差、格式化需求(小數位數、百分號、千分位)等問題。本文提供生產級工具方法,涵蓋基本計算、精度控制、格式化輸出與邊界處理。

核心工具類

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;

public class PercentUtils {

    private static final DecimalFormat DEFAULT_FMT = new DecimalFormat("##.00%");
    private static final DecimalFormat PCT_FMT_2 = new DecimalFormat("0.00%");
    private static final DecimalFormat PCT_FMT_4 = new DecimalFormat("0.0000%");

    /**
     * 計算百分比並格式化(預設保留兩位小數,含百分號)
     * @param numerator 分子
     * @param denominator 分母
     * @return 格式化字串,如 "49.15%"
     * @throws ArithmeticException 分母為零
     */
    public static String formatPercent(long numerator, long denominator) {
        if (denominator == 0) throw new ArithmeticException("分母不能為零");
        double ratio = (double) numerator / denominator;
        return DEFAULT_FMT.format(ratio);
    }

    /**
     * 高精度計算(金融/統計場景),使用 BigDecimal
     * @param numerator 分子
     * @param denominator 分母
     * @param scale 保留小數位數
     * @param rounding 舍入模式
     * @return BigDecimal 比率(0~1 之間),未乘 100
     */
    public static BigDecimal calculateRatio(BigDecimal numerator, BigDecimal denominator,
                                            int scale, RoundingMode rounding) {
        if (denominator.compareTo(BigDecimal.ZERO) == 0)
            throw new ArithmeticException("分母不能為零");
        return numerator.divide(denominator, scale, rounding);
    }

    /**
     * 格式化 BigDecimal 比率為百分比字串
     * @param ratio 0~1 之間的比率
     * @param scale 百分比小數位數
     * @return 如 "49.1523%"
     */
    public static String formatRatio(BigDecimal ratio, int scale) {
        DecimalFormat fmt = new DecimalFormat("0." + "0".repeat(scale) + "%");
        // ratio * 100
        return fmt.format(ratio.multiply(BigDecimal.valueOf(100)));
    }

    /** 便利方法:long 參數,指定小數位 */
    public static String formatPercent(long num, long den, int scale) {
        if (den == 0) throw new ArithmeticException("分母不能為零");
        DecimalFormat fmt = new DecimalFormat("0." + "0".repeat(scale) + "%");
        double ratio = (double) num / den;
        return fmt.format(ratio);
    }

    /** 便利方法:double 參數 */
    public static String formatPercent(double ratio, int scale) {
        DecimalFormat fmt = new DecimalFormat("0." + "0".repeat(scale) + "%");
        return fmt.format(ratio);
    }
}

使用範例

public class PercentDemo {
    public static void main(String[] args) {
        // 1. 基本用法
        System.out.println(PercentUtils.formatPercent(29, 59));        // 49.15%
        System.out.println(PercentUtils.formatPercent(1, 3, 4));       // 33.3333%
        System.out.println(PercentUtils.formatPercent(0.12345, 2));    // 12.35%

        // 2. 高精度金融計算
        BigDecimal num = new BigDecimal("12345.67");
        BigDecimal den = new BigDecimal("98765.43");
        BigDecimal ratio = PercentUtils.calculateRatio(num, den, 6, RoundingMode.HALF_UP);
        System.out.println("Ratio: " + ratio);                         // 0.124999
        System.out.println("Percent: " + PercentUtils.formatRatio(ratio, 4)); // 12.4999%

        // 3. 邊界測試
        try { PercentUtils.formatPercent(10, 0); }
        catch (ArithmeticException e) { System.out.println("捕獲異常: " + e.getMessage()); }

        // 4. 大數值
        System.out.println(PercentUtils.formatPercent(123456789L, 987654321L, 2)); // 12.50%
    }
}

關鍵技術點解析

問題解決方案
整數相除截斷先轉 double 或乘 1.0:num * 1.0 / den
浮點數精度誤差(如 0.1+0.2)金融/統計場景改用 BigDecimal,字串建構避免二進位誤差
格式化小數位不固定DecimalFormat("0.00%") 強制兩位;"##.00%" 整數部分不補零
四捨五入模式需求BigDecimal.divide(scale, RoundingMode.HALF_UP) 等 8 種模式可選
分母為零顯式檢查拋出 ArithmeticException,避免 Infinity 污染下游
負數百分比顯示DecimalFormat 自動處理負號,格式如 -12.50%

DecimalFormat 模式速查

0      - 數字,不足位補 0
#      - 數字,不足位不補
.      - 小數點
%      - 乘 100 並顯示百分號
,      - 千分位分隔符
;      - 分隔正負數模式(如 "0.00%;-0.00%")

效能建議

  • DecimalFormat 非執行緒安全,不要 作為 static 共用實例於多執行緒環境。改用 ThreadLocal 或每次新建(輕量)。
  • 高頻呼叫熱點路徑可改用 String.format("%.2f%%", ratio * 100),但精度控制較弱。
  • BigDecimal 物件創建開銷較大,非金融場景優先用 double + DecimalFormat。

結語

百分比計算的核心在於精度選型(double vs BigDecimal)與格式化模式(DecimalFormat)。封裝通用工具類並覆蓋邊界測試,可消除專案中散落的重複邏輯與潛在 Bug,提升程式碼可維護性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved