最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文淺析Java金額計(jì)算用long還是BigDecimal

 更新時(shí)間:2025年12月26日 09:30:52   作者:劉大華  
在Java后端開發(fā)中處理與錢有關(guān)的業(yè)務(wù)時(shí),確保金額計(jì)算的準(zhǔn)確性和避免錯(cuò)誤非常重要,這篇文章主要給大家介紹了關(guān)于Java中處理金額計(jì)算之使用Long還是BigDecimal的相關(guān)資料,希望對(duì)大家有所幫助

前言

最近接觸一個(gè)新項(xiàng)目,發(fā)現(xiàn)系統(tǒng)中所有金額相關(guān)字段都使用long類型來表示。

作為一個(gè)習(xí)慣使用BigDecimal處理金額的開發(fā)者,這讓我產(chǎn)生了疑惑:這會(huì)不會(huì)有精度問題?為什么要這樣設(shè)計(jì)?

“用double不行嗎?它也能表示小數(shù)?。?rdquo;

經(jīng)過一番研究和思考,把最終得出的結(jié)論來和大家詳細(xì)分享一下。

一、double、float類型比較

doublefloat都是Java中的浮點(diǎn)數(shù)類型,它們采用IEEE 754標(biāo)準(zhǔn)來表示小數(shù)。

這種表示方法類似于科學(xué)計(jì)數(shù)法,但存在一個(gè)致命問題:不是所有小數(shù)都能精確表示。

舉一個(gè)簡(jiǎn)單的例子:

double a = 0.1;
double b = 0.2;
double result = a + b;
System.out.println(result); 
// 輸出:0.30000000000000004

什么?簡(jiǎn)單的0.1 + 0.2居然不等于0.3?

是的,這是因?yàn)樵诙M(jìn)制的計(jì)算中,有些十進(jìn)制小數(shù)沒有辦法精確表示,就像1除以3一樣,在十進(jìn)制中也不能精確表示(0.33333...)。

二、BigDecimal 解決方案

BigDecimal的正確用法

BigDecimal可以解決精度問題,但必須正確使用。

錯(cuò)誤的構(gòu)造方式

BigDecimal wrong1 = new BigDecimal(0.1); 
BigDecimal wrong2 = new BigDecimal(0.2);

這種傳入浮點(diǎn)型的構(gòu)造方式還是會(huì)有精度問題。

正確的構(gòu)造方式

BigDecimal correct1 = new BigDecimal("0.1");
BigDecimal correct2 = new BigDecimal("0.2");
System.out.println("正確方式: " + correct1.add(correct2)); // 0.3

通過傳入字符串進(jìn)行構(gòu)造。

或者用valueOf(內(nèi)部也是轉(zhuǎn)字符串)

BigDecimal safe1 = BigDecimal.valueOf(0.1);
BigDecimal safe2 = BigDecimal.valueOf(0.2);
System.out.println("安全方式: " + safe1.add(safe2)); // 0.3

BigDecimal的運(yùn)算規(guī)則

public class BigDecimalOperations {
    public static void main(String[] args) {
        BigDecimal price = new BigDecimal("19.99");
        BigDecimal quantity = new BigDecimal("3");
        BigDecimal taxRate = new BigDecimal("0.13");
        
        // 乘法
        BigDecimal subtotal = price.multiply(quantity);
        
        // 除法必須指定精度和舍入模式
        BigDecimal tax = subtotal.multiply(taxRate)
                               .setScale(2, RoundingMode.HALF_UP);
        
        // 加法
        BigDecimal total = subtotal.add(tax);
        
        System.out.println("小計(jì): " + subtotal); // 59.97
        System.out.println("稅金: " + tax);      // 7.80
        System.out.println("總計(jì): " + total);    // 67.77
    }
}

BigDecimal的比較操作

public class BigDecimalComparison {
    public static void main(String[] args) {
        BigDecimal num1 = new BigDecimal("10.00");
        BigDecimal num2 = new BigDecimal("10.000");
        
        // 錯(cuò)誤:比較引用
        System.out.println("== 比較: " + (num1 == num2)); // false
        
        // 錯(cuò)誤:equals會(huì)比較精度
        System.out.println("equals比較: " + num1.equals(num2)); // false
        
        // 正確:compareTo只比較數(shù)值
        System.out.println("compareTo比較: " + (num1.compareTo(num2) == 0)); // true
    }
}

BigDecimal的缺點(diǎn)

性能開銷:對(duì)象創(chuàng)建和運(yùn)算成本高

內(nèi)存占用:每個(gè)對(duì)象需要20-30字節(jié)

使用復(fù)雜:容易用錯(cuò)構(gòu)造方法和運(yùn)算規(guī)則

代碼冗長(zhǎng):簡(jiǎn)單的運(yùn)算也需要多行代碼

三、long方案

核心思想:以分為單位存儲(chǔ)

使用long表示金額的核心思路是:不以元為單位,而以最小貨幣單位(分)存儲(chǔ)。

public class LongAmountDemo {
    // 工具方法:元轉(zhuǎn)分
    public static long yuanToFen(double yuan) {
        return Math.round(yuan * 100);
    }
    
    // 工具方法:分轉(zhuǎn)元(用于顯示)
    public static String fenToYuanDisplay(long fen) {
        return String.format("¥%.2f", fen / 100.0);
    }
    
    // 工具方法:分轉(zhuǎn)元(用于計(jì)算)
    public static double fenToYuan(long fen) {
        return fen / 100.0;
    }
    
    public static void main(String[] args) {
        // 以分為單位存儲(chǔ)所有金額
        long price = yuanToFen(19.99);  // 1999分 = 19.99元
        long quantity = 3;
        long taxRate = 13;  // 13% = 0.13,這里用整數(shù)表示百分比
        
        // 計(jì)算過程全部使用整數(shù)運(yùn)算
        long subtotal = price * quantity;                    // 5997分
        long tax = (subtotal * taxRate) / 100;               // 780分
        long total = subtotal + tax;                         // 6777分
        
        System.out.println("小計(jì): " + fenToYuanDisplay(subtotal)); // ¥59.97
        System.out.println("稅金: " + fenToYuanDisplay(tax));      // ¥7.80
        System.out.println("總計(jì): " + fenToYuanDisplay(total));    // ¥67.77
    }
}

long方案的優(yōu)勢(shì)

1.絕對(duì)精確 整數(shù)運(yùn)算在計(jì)算機(jī)中是精確的,不會(huì)出現(xiàn)浮點(diǎn)數(shù)的精度問題。

2.性能卓越

// long運(yùn)算 - 機(jī)器指令級(jí)別,極快
long a = 1000L, b = 2000L;
long result = a + b;

// BigDecimal運(yùn)算 - 方法調(diào)用,對(duì)象創(chuàng)建,較慢
BigDecimal c = new BigDecimal("10.00");
BigDecimal d = new BigDecimal("20.00");
BigDecimal result2 = c.add(d);

3.存儲(chǔ)高效

  • long:固定8字節(jié)
  • BigDecimal:對(duì)象頭+數(shù)值,通常20-30字節(jié)

4.序列化簡(jiǎn)單 在數(shù)據(jù)庫(kù)、JSON、網(wǎng)絡(luò)傳輸中處理更簡(jiǎn)單。

實(shí)際業(yè)務(wù)應(yīng)用

public class OrderService {
    // 訂單金額(分)
    private long orderAmount;
    // 優(yōu)惠金額(分)
    private long discountAmount;
    // 實(shí)付金額(分)
    private long actualAmount;
    
    public void calculateOrder(long unitPrice, int quantity, long discountRate) {
        // 計(jì)算訂單金額
        orderAmount = unitPrice * quantity;
        
        // 計(jì)算優(yōu)惠金額
        discountAmount = (orderAmount * discountRate) / 100;
        
        // 計(jì)算實(shí)付金額
        actualAmount = orderAmount - discountAmount;
    }
    
    // 顯示方法
    public String getDisplayAmount() {
        return String.format("訂單金額: %s, 優(yōu)惠: %s, 實(shí)付: %s", 
            formatFen(orderAmount),
            formatFen(discountAmount),
            formatFen(actualAmount));
    }
    
    private String formatFen(long fen) {
        return String.format("¥%.2f", fen / 100.0);
    }
}

四、各種方案的適用場(chǎng)景

double/float:絕對(duì)不要用于金額

  • 科學(xué)計(jì)算、圖形處理
  • 統(tǒng)計(jì)分析(允許誤差)
  • 物理模擬
  • 不適用于任何金額計(jì)算

BigDecimal:復(fù)雜金融計(jì)算

  • 高精度利率、匯率計(jì)算
  • 稅 務(wù)計(jì)算(需要復(fù)雜小數(shù)運(yùn)算)
  • 銀行核心系統(tǒng)
  • 需要任意精度的場(chǎng)景

long:大多數(shù)業(yè)務(wù)系統(tǒng)

  • 電商訂單系統(tǒng)
  • 支付系統(tǒng)
  • 賬戶余額管理
  • 積分、優(yōu)惠券系統(tǒng)

五、BigDecimal的最佳實(shí)踐

如果確實(shí)需要使用BigDecimal,請(qǐng)遵循以下規(guī)則:

1. 構(gòu)造方法

// 推薦
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = BigDecimal.valueOf(0.1);  // 內(nèi)部轉(zhuǎn)字符串

// 避免
BigDecimal c = new BigDecimal(0.1);      // 精度問題

2. 運(yùn)算控制

BigDecimal num1 = new BigDecimal("10.00");
BigDecimal num2 = new BigDecimal("3.00");

// 除法必須指定精度
BigDecimal result = num1.divide(num2, 4, RoundingMode.HALF_UP);

// 乘法建議控制精度
BigDecimal product = num1.multiply(num2).setScale(2, RoundingMode.HALF_UP);

3. 數(shù)值比較

BigDecimal amount1 = new BigDecimal("100.00");
BigDecimal amount2 = new BigDecimal("100.000");

// 正確
if (amount1.compareTo(amount2) == 0) {
    // 數(shù)值相等
}

// 錯(cuò)誤
if (amount1.equals(amount2)) {
    // 不會(huì)執(zhí)行,因?yàn)榫炔煌?
}

六、long方案的實(shí)施建議

1. 建立工具類

public class MoneyUtils {
    private MoneyUtils() {} // 工具類,防止實(shí)例化
    
    // 元轉(zhuǎn)分
    public static long yuanToFen(double yuan) {
        return Math.round(yuan * 100);
    }
    
    // 分轉(zhuǎn)元(顯示用)
    public static String fenToDisplayYuan(long fen) {
        return String.format("¥%.2f", fen / 100.0);
    }
    
    // 分轉(zhuǎn)元(計(jì)算用)
    public static double fenToYuan(long fen) {
        return fen / 100.0;
    }
    
    // 金額加法(防止溢出)
    public static long add(long amount1, long amount2) {
        return Math.addExact(amount1, amount2);
    }
    
    // 金額乘法
    public static long multiply(long amount, int multiplier) {
        return Math.multiplyExact(amount, multiplier);
    }
}

2. 數(shù)據(jù)庫(kù)設(shè)計(jì)

-- 使用bigint存儲(chǔ)金額(分)
CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    order_amount BIGINT COMMENT '訂單金額(分)',
    discount_amount BIGINT COMMENT '優(yōu)惠金額(分)',
    actual_amount BIGINT COMMENT '實(shí)付金額(分)'
);

3. API設(shè)計(jì)

// 請(qǐng)求和響應(yīng)中使用Long類型表示金額(分)
public class OrderRequest {
    private Long productId;
    private Integer quantity;
    private Long unitPrice;  // 單價(jià)(分)
}

public class OrderResponse {
    private String orderNo;
    private Long totalAmount;  // 總金額(分)
    private String displayAmount; // 顯示金額 "¥99.99"
}

總結(jié)與選擇

為什么那個(gè)項(xiàng)目選擇long?

現(xiàn)在我可以理解那個(gè)項(xiàng)目的設(shè)計(jì)思路了:

  • 業(yè)務(wù)特性:主要是簡(jiǎn)單的加減乘運(yùn)算,沒有復(fù)雜的小數(shù)除法
  • 性能要求:高并發(fā)場(chǎng)景,需要最優(yōu)性能
  • 團(tuán)隊(duì)協(xié)作:統(tǒng)一規(guī)范,避免BigDecimal的誤用
  • 系統(tǒng)復(fù)雜度:降低系統(tǒng)復(fù)雜度和維護(hù)成本

如何選擇

場(chǎng)景推薦方案理由
簡(jiǎn)單業(yè)務(wù)系統(tǒng)long性能好,簡(jiǎn)單可靠
銀行金融系統(tǒng)BigDecimal精度要求極高
高并發(fā)交易long性能至關(guān)重要
復(fù)雜稅 務(wù)計(jì)算BigDecimal需要復(fù)雜小數(shù)運(yùn)算
新項(xiàng)目啟動(dòng)long技術(shù)債務(wù)少

技術(shù)選型從來都不是單一的,理解業(yè)務(wù)的需求,能選擇出最適合的技術(shù)方案就是最好的方案。

以上就是一文淺析Java金額計(jì)算用long還是BigDecimal的詳細(xì)內(nèi)容,更多關(guān)于Java計(jì)算金額的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中的synchronized鎖膨脹詳解

    Java中的synchronized鎖膨脹詳解

    這篇文章主要介紹了Java中的synchronized鎖膨脹詳解,正常創(chuàng)建的對(duì)象,狀態(tài)為無鎖,對(duì)象頭的Mark?Word?中主要記錄了?對(duì)象的年齡,也就是經(jīng)歷了多少次GC還存活下來,需要的朋友可以參考下
    2024-01-01
  • SpringBoot打印Banner的實(shí)現(xiàn)示例

    SpringBoot打印Banner的實(shí)現(xiàn)示例

    本文主要介紹了SpringBoot啟動(dòng)Banner的實(shí)現(xiàn)原理和打印流程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-01-01
  • Java中最基本的存儲(chǔ)單元之變量基礎(chǔ)教程

    Java中最基本的存儲(chǔ)單元之變量基礎(chǔ)教程

    變量相當(dāng)于內(nèi)存中一個(gè)數(shù)據(jù)存儲(chǔ)空間的表示,類似與門牌號(hào)與房間的關(guān)系—變量看作門牌號(hào),通過門牌號(hào)可以找到房間,而通過變量名可以訪問到變量,這篇文章主要介紹了Java中最基本的存儲(chǔ)單元之變量基礎(chǔ)教程的相關(guān)資料,需要的朋友可以參考下
    2025-12-12
  • java常用的定時(shí)任務(wù)框架及相關(guān)代碼應(yīng)用示例

    java常用的定時(shí)任務(wù)框架及相關(guān)代碼應(yīng)用示例

    在Java開發(fā)中,定時(shí)任務(wù)是一個(gè)常見的需求,比如定時(shí)備份數(shù)據(jù)、定時(shí)清理日志、定時(shí)發(fā)送郵件等,這篇文章主要介紹了java常用的定時(shí)任務(wù)框架及相關(guān)代碼應(yīng)用的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-11-11
  • Java圖片與Base64互轉(zhuǎn)工具類實(shí)現(xiàn)過程

    Java圖片與Base64互轉(zhuǎn)工具類實(shí)現(xiàn)過程

    本文介紹了Java中實(shí)現(xiàn)圖片與Base64編碼轉(zhuǎn)換的工具類,包括本地圖片轉(zhuǎn)Base64編碼、網(wǎng)絡(luò)圖片轉(zhuǎn)Base64編碼、Base64編碼轉(zhuǎn)圖片文件等功能,工具類使用了Java標(biāo)準(zhǔn)庫(kù)和Apache Commons庫(kù),通過Java NIO實(shí)現(xiàn)高效的文件操作,感興趣的朋友跟隨小編一起看看吧
    2026-01-01
  • Java實(shí)現(xiàn)Markdown圖片批量本地化處理工具

    Java實(shí)現(xiàn)Markdown圖片批量本地化處理工具

    在日常使用Markdown時(shí),我們常通過遠(yuǎn)程URL引用圖片,所以本文小編就為大家詳細(xì)介紹一下如何使用Java實(shí)現(xiàn)Markdown圖片批量本地化處理吧
    2025-07-07
  • SpringBoot整合Shiro的方法詳解

    SpringBoot整合Shiro的方法詳解

    Apache?Shiro是一個(gè)java安全(權(quán)限)框架,Shiro可以非常容易的開發(fā)出足夠好的應(yīng)用,其不僅可以用在javase環(huán)境,也可以用在javaee環(huán)境。本文介紹了SpringBoot整合Shiro的方法,需要的可以參考一下
    2022-05-05
  • java面向?qū)ο蟮娜筇匦灾焕^承用法實(shí)例分析

    java面向?qū)ο蟮娜筇匦灾焕^承用法實(shí)例分析

    這篇文章主要介紹了java面向?qū)ο蟮娜筇匦灾焕^承用法,結(jié)合實(shí)例形式分析了java面向?qū)ο蟪绦蛟O(shè)計(jì)中繼承的基本原理與具體使用方法,需要的朋友可以參考下
    2019-11-11
  • Java 實(shí)現(xiàn)分布式服務(wù)的調(diào)用鏈跟蹤

    Java 實(shí)現(xiàn)分布式服務(wù)的調(diào)用鏈跟蹤

    分布式服務(wù)中完成某一個(gè)業(yè)務(wù)動(dòng)作,需要服務(wù)之間的相互協(xié)作才能完成,在這一次動(dòng)作引起的多服務(wù)的聯(lián)動(dòng)我們需要用1個(gè)唯一標(biāo)識(shí)關(guān)聯(lián)起來,關(guān)聯(lián)起來就是調(diào)用鏈的跟蹤。本文介紹了Java 實(shí)現(xiàn)分布式服務(wù)的調(diào)用鏈跟蹤的步驟
    2021-06-06
  • MyBatis Plus插件機(jī)制與執(zhí)行流程原理分析詳解

    MyBatis Plus插件機(jī)制與執(zhí)行流程原理分析詳解

    這篇文章主要介紹了MyBatis Plus插件機(jī)制與執(zhí)行流程原理分析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09

最新評(píng)論

呼和浩特市| 鄂尔多斯市| 舒城县| 木里| 永寿县| 普陀区| 贵州省| 敖汉旗| 北海市| 房山区| 焦作市| 德州市| 泰宁县| 宝丰县| 宣恩县| 长岛县| 靖安县| 广宁县| 孟津县| 来安县| 罗源县| 垣曲县| 黄龙县| 息烽县| 德州市| 铁岭市| 千阳县| 万年县| 舒城县| 昌吉市| 慈利县| 临汾市| 册亨县| 靖宇县| 霍林郭勒市| 闽清县| 志丹县| 光泽县| 龙胜| 濮阳县| 和田县|