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

Java使用BigDecimal解決精度丟失問題

 更新時間:2026年04月27日 09:44:45   作者:Java女俠_9年實戰(zhàn)  
本文詳細解釋了浮點型數(shù)據(jù)因二進制和十進制不兼容而導致的精度丟失問題,并指出BigDecimal是解決該問題的有效方法,文中的示例代碼講解詳細,感興趣的小伙伴可以了解下

一、先搞懂:為什么浮點型會丟精度?

我們常用的floatdouble,屬于二進制浮點類型,但現(xiàn)實中我們計算的“金額(如1.2元)”“重量(如3.5kg)”是十進制數(shù)據(jù)——這兩種進制的“不兼容”,就是精度丟失的根源。

舉個最直觀的例子,運行這段代碼:

public class FloatTest {
    public static void main(String[] args) {
        System.out.println(0.1 + 0.2); // 輸出結果不是0.3,而是0.30000000000000004
        System.out.println(1.0 - 0.9); // 輸出0.09999999999999998
        System.out.println(2.01 * 100); // 輸出200.99999999999997
    }
}

為什么會這樣?

因為0.1在二進制中是“無限循環(huán)小數(shù)” (類似十進制的1/3=0.333...),而floatdouble的存儲位數(shù)有限(float占4字節(jié),double占8字節(jié)),只能“四舍五入”保留部分二進制位——這種“截斷”就導致了精度丟失,后續(xù)計算會把誤差放大,最終出現(xiàn)“0.1+0.2≠0.3”的詭異結果。

重點:只要涉及“需要精確計算”的場景(金額、財務、計量),絕對不能用float/double!  這不是“代碼寫錯了”,而是數(shù)據(jù)類型的底層特性決定的。

二、救星BigDecimal:但90%的人用錯了

很多人知道“用BigDecimal代替浮點型”,但我見過太多“用了BigDecimal還丟精度”的情況——問題出在構造方法和計算方式上,這兩個坑一定要避開。

坑1:用double構造BigDecimal(最常見錯誤)

直接用new BigDecimal(double)構造,會把double的精度誤差“帶進去”,比如:

// 錯誤用法:用double構造,精度誤差被保留
BigDecimal wrong1 = new BigDecimal(0.1);
System.out.println(wrong1); // 輸出0.1000000000000000055511151231257827021181583404541015625
// 正確用法:用String構造,完全保留十進制精度
BigDecimal right1 = new BigDecimal("0.1");
System.out.println(right1); // 輸出0.1
// 也可以用BigDecimal.valueOf(double)(底層會轉成String,推薦)
BigDecimal right2 = BigDecimal.valueOf(0.1);
System.out.println(right2); // 輸出0.1

結論:構造BigDecimal時,優(yōu)先用new BigDecimal(String)BigDecimal.valueOf(double),絕對別用new BigDecimal(double)!

坑2:用“+、-、*、/”直接計算(編譯都不通過)

BigDecimal是“對象”,不能像基本類型那樣用算術運算符計算,必須用它的成員方法add/subtract/multiply/divide),且計算時要指定“舍入模式”(避免除不盡時拋異常)。

正確計算示例(以金額計算為例):

public class BigDecimalCalc {
    public static void main(String[] args) {
        // 1. 初始化金額(單位:元,用String構造)
        BigDecimal price = new BigDecimal("99.9"); // 商品單價
        BigDecimal quantity = new BigDecimal("3"); // 購買數(shù)量
        BigDecimal discount = new BigDecimal("0.9"); // 9折優(yōu)惠
        
        // 2. 計算:總價 = 單價 * 數(shù)量 * 折扣(用成員方法)
        BigDecimal total = price.multiply(quantity).multiply(discount);
        System.out.println("折后總價:" + total); // 輸出269.73
        
        // 3. 除法示例(如拆分金額,必須指定舍入模式)
        BigDecimal split = total.divide(new BigDecimal("2"), 2, BigDecimal.ROUND_HALF_UP);
        System.out.println("每人分攤:" + split); // 輸出134.87(四舍五入保留2位小數(shù))
    }
}

關鍵:舍入模式怎么選?(金額計算常用3種)

舍入模式常量含義(以保留2位小數(shù)為例)適用場景
ROUND_HALF_UP四舍五入(1.234→1.23,1.235→1.24)金額計算、日常統(tǒng)計
ROUND_DOWN直接截斷(1.239→1.23)計算最小支付金額(不進位)
ROUND_UP直接進位(1.231→1.24)計算稅費(不遺漏分厘)

注意:divide方法必須指定“舍入模式”和“保留小數(shù)位數(shù)”,否則當除法結果是無限小數(shù)時(如1÷3),會拋出ArithmeticException!

三、實戰(zhàn)避坑:金額計算的3個“固定套路”

結合8年項目經(jīng)驗,總結出“金額計算(元為單位)”的標準化寫法,直接套用能避免99%的問題:

套路1:定義“保留小數(shù)位數(shù)”和“舍入模式”常量

避免硬編碼,后續(xù)修改更方便:

// 金額計算:固定保留2位小數(shù),四舍五入
private static final int SCALE = 2;
private static final int ROUND_MODE = BigDecimal.ROUND_HALF_UP;

套路2:封裝“加減乘除”工具方法

重復代碼抽成工具類,減少重復錯誤:

public class MoneyUtil {
    private static final int SCALE = 2;
    private static final int ROUND_MODE = BigDecimal.ROUND_HALF_UP;

    // 加法
    public static BigDecimal add(BigDecimal a, BigDecimal b) {
        return a.add(b).setScale(SCALE, ROUND_MODE);
    }

    // 減法
    public static BigDecimal subtract(BigDecimal a, BigDecimal b) {
        return a.subtract(b).setScale(SCALE, ROUND_MODE);
    }

    // 乘法
    public static BigDecimal multiply(BigDecimal a, BigDecimal b) {
        return a.multiply(b).setScale(SCALE, ROUND_MODE);
    }

    // 除法
    public static BigDecimal divide(BigDecimal a, BigDecimal b) {
        if (BigDecimal.ZERO.compareTo(b) == 0) {
            throw new IllegalArgumentException("除數(shù)不能為0");
        }
        return a.divide(b, SCALE, ROUND_MODE);
    }
}

套路3:和數(shù)據(jù)庫交互時的“類型對應”

如果數(shù)據(jù)庫存儲金額用DECIMAL類型(推薦),Java中用BigDecimal接收,避免類型轉換丟失精度:

  • 數(shù)據(jù)庫字段定義:amount DECIMAL(10,2)(10位整數(shù)+2位小數(shù),足夠存儲千萬級金額)
  • MyBatis映射:直接用java.math.BigDecimal對應,不要用double接收

四、知識擴展

在 Java 中,float 和 double 用于科學計算和工程計算,但由于它們采用二進制浮點數(shù)表示,無法精確表示某些十進制小數(shù)(如 0.1),從而導致精度丟失。

示例:

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

為了進行精確的十進制運算(如金額計算),Java 提供了 BigDecimal 類。它通過用整數(shù)(BigInteger)和標度(小數(shù)點后位數(shù))的組合來精確表示任意精度的十進制數(shù)。

正確創(chuàng)建 BigDecimal 對象

錯誤方式:使用 double 構造方法

BigDecimal bd = new BigDecimal(0.1); // 0.1000000000000000055511151231257827021181583404541015625

因為 0.1 本身已經(jīng)是近似值,所以傳入 double 就失去了精度。

正確方式:使用字符串構造器

BigDecimal bd = new BigDecimal("0.1");

推薦方式:使用 BigDecimal.valueOf(double)如果參數(shù)是 double 字面量或已知精確值,可使用靜態(tài)方法:

BigDecimal bd = BigDecimal.valueOf(0.1);

該方法內部先將 double 轉換為字符串再構造,能避免部分精度問題。

基本運算(加減乘除)

BigDecimal a = new BigDecimal("10.25");
BigDecimal b = new BigDecimal("3.05");

// 加法
BigDecimal sum = a.add(b);          // 13.30

// 減法
BigDecimal diff = a.subtract(b);    // 7.20

// 乘法
BigDecimal prod = a.multiply(b);    // 31.2625

// 除法(必須指定舍入模式,否則可能拋出 ArithmeticException)
BigDecimal quot = a.divide(b, 2, RoundingMode.HALF_UP); // 3.36
// 參數(shù):除數(shù),保留小數(shù)位數(shù),舍入模式

關鍵:除法必須指定精度和舍入模式,避免無限循環(huán)小數(shù)。

精度控制與舍入模式

舍入模式說明
RoundingMode.HALF_UP四舍五入(最常用)
RoundingMode.HALF_DOWN五舍六入
RoundingMode.UP遠離零方向舍入
RoundingMode.DOWN向零方向舍入
RoundingMode.CEILING向正無窮方向舍入
RoundingMode.FLOOR向負無窮方向舍入
RoundingMode.HALF_EVEN向偶數(shù)舍入(銀行家舍入法)
BigDecimal d = new BigDecimal("2.335");
System.out.println(d.setScale(2, RoundingMode.HALF_UP));   // 2.34
System.out.println(d.setScale(2, RoundingMode.HALF_DOWN)); // 2.33

比較 BigDecimal 對象

錯誤方式:使用 equalsequals 會比較精度(scale),例如 new BigDecimal("2.0") 與 new BigDecimal("2.00") 不相等。

正確方式:使用 compareTo

BigDecimal x = new BigDecimal("2.0");
BigDecimal y = new BigDecimal("2.00");
System.out.println(x.compareTo(y) == 0); // true

compareTo 返回 -1(小于)、0(等于)、1(大于)。

五、最后總結:3句話避開浮點精度坑

  • 場景判斷:只要是“需要精確到分/厘”的計算(金額、財務),絕對不用float/double;
  • 構造正確:BigDecimal用String構造或valueOf(double),別用double構造;
  • 計算規(guī)范:用成員方法(add/subtract),指定舍入模式和保留位數(shù),優(yōu)先封裝工具類。

到此這篇關于Java使用BigDecimal解決精度丟失問題的文章就介紹到這了,更多相關Java BigDecimal解決精度丟失內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java集合中的Iterator迭代器接口詳解

    Java集合中的Iterator迭代器接口詳解

    這篇文章主要介紹了Java集合中的Iterator迭代器接口詳解,Iterator,被稱為迭代器接口,本身并不提供存儲對象的能力,主要用于遍歷Collection中的元素,需要的朋友可以參考下
    2023-11-11
  • Java使用正則表達式演示電話與郵箱格式

    Java使用正則表達式演示電話與郵箱格式

    本文詳細講解了Java正則表達式的用法,舉例演示電話和郵箱格式,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-05-05
  • 談談我對Spring Bean 生命周期的理解

    談談我對Spring Bean 生命周期的理解

    Spring Bean 的生命周期在整個 Spring 中占有很重要的位置,掌握這些可以加深對 Spring 的理解。這篇文章主要介紹了Spring Bean 生命周期,需要的朋友可以參考下
    2018-03-03
  • java圖的深度優(yōu)先遍歷實現(xiàn)隨機生成迷宮

    java圖的深度優(yōu)先遍歷實現(xiàn)隨機生成迷宮

    這篇文章主要為大家詳細介紹了java圖的深度優(yōu)先遍歷實現(xiàn)隨機生成迷宮,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • idea2020.3.3集成maven及遇到的坑(推薦)

    idea2020.3.3集成maven及遇到的坑(推薦)

    這篇文章主要介紹了idea2020.3.3集成maven的相關知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • 面向對象編程:Java中的抽象數(shù)據(jù)類型

    面向對象編程:Java中的抽象數(shù)據(jù)類型

    面向對象編程:Java中的抽象數(shù)據(jù)類型...
    2006-12-12
  • Java數(shù)據(jù)結構之平衡二叉樹的實現(xiàn)詳解

    Java數(shù)據(jù)結構之平衡二叉樹的實現(xiàn)詳解

    平衡二叉樹又被稱為AVL樹(有別于AVL算法),且具有以下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,并且左右兩個子樹都是一棵平衡二叉樹。本文將詳解介紹一下平衡二叉樹的原理與實現(xiàn),需要的可以參考一下
    2022-03-03
  • Java集合List相關面試題整理大全

    Java集合List相關面試題整理大全

    這篇文章主要給大家介紹了關于Java集合List相關面試題整理的相關資料,下面將提供一些常見的Java集合類面試題及其解答,幫助讀者更好地準備面試,需要的朋友可以參考下
    2024-01-01
  • Spring boot如何通過@Scheduled實現(xiàn)定時任務及多線程配置

    Spring boot如何通過@Scheduled實現(xiàn)定時任務及多線程配置

    這篇文章主要介紹了Spring boot如何通過@Scheduled實現(xiàn)定時任務及多線程配置,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-12-12
  • Java并發(fā)工具Fork/Join原理

    Java并發(fā)工具Fork/Join原理

    這篇文章主要為大家介紹了Java并發(fā)工具Fork/Join原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06

最新評論

名山县| 长顺县| 南漳县| 精河县| 富平县| 沙田区| 东源县| 通辽市| 安阳市| 星子县| 马尔康县| 潮安县| 古丈县| 永兴县| 子洲县| 应用必备| 岚皋县| 华阴市| 汶川县| 徐汇区| 特克斯县| 葵青区| 普安县| 高唐县| 泸水县| 江达县| 北辰区| 竹溪县| 桦南县| 麻城市| 梅州市| 木兰县| 六枝特区| 象山县| 霍林郭勒市| 新安县| 利辛县| 汶川县| 扎鲁特旗| 长宁区| 平陆县|