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

Java使用BigDecimal解決精度丟失問(wèn)題

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

一、先搞懂:為什么浮點(diǎn)型會(huì)丟精度?

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

舉個(gè)最直觀的例子,運(yùn)行這段代碼:

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

為什么會(huì)這樣?

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

重點(diǎn):只要涉及“需要精確計(jì)算”的場(chǎng)景(金額、財(cái)務(wù)、計(jì)量),絕對(duì)不能用float/double!  這不是“代碼寫(xiě)錯(cuò)了”,而是數(shù)據(jù)類型的底層特性決定的。

二、救星BigDecimal:但90%的人用錯(cuò)了

很多人知道“用BigDecimal代替浮點(diǎn)型”,但我見(jiàn)過(guò)太多“用了BigDecimal還丟精度”的情況——問(wèn)題出在構(gòu)造方法和計(jì)算方式上,這兩個(gè)坑一定要避開(kāi)。

坑1:用double構(gòu)造BigDecimal(最常見(jiàn)錯(cuò)誤)

直接用new BigDecimal(double)構(gòu)造,會(huì)把double的精度誤差“帶進(jìn)去”,比如:

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

結(jié)論:構(gòu)造BigDecimal時(shí),優(yōu)先用new BigDecimal(String)BigDecimal.valueOf(double),絕對(duì)別用new BigDecimal(double)

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

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

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

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

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

舍入模式常量含義(以保留2位小數(shù)為例)適用場(chǎng)景
ROUND_HALF_UP四舍五入(1.234→1.23,1.235→1.24)金額計(jì)算、日常統(tǒng)計(jì)
ROUND_DOWN直接截?cái)啵?.239→1.23)計(jì)算最小支付金額(不進(jìn)位)
ROUND_UP直接進(jìn)位(1.231→1.24)計(jì)算稅費(fèi)(不遺漏分厘)

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

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

結(jié)合8年項(xiàng)目經(jīng)驗(yàn),總結(jié)出“金額計(jì)算(元為單位)”的標(biāo)準(zhǔn)化寫(xiě)法,直接套用能避免99%的問(wèn)題:

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

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

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

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

重復(fù)代碼抽成工具類,減少重復(fù)錯(cuò)誤:

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ù)庫(kù)交互時(shí)的“類型對(duì)應(yīng)”

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

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

四、知識(shí)擴(kuò)展

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

示例:

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

為了進(jìn)行精確的十進(jìn)制運(yùn)算(如金額計(jì)算),Java 提供了 BigDecimal 類。它通過(guò)用整數(shù)(BigInteger)和標(biāo)度(小數(shù)點(diǎn)后位數(shù))的組合來(lái)精確表示任意精度的十進(jìn)制數(shù)。

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

錯(cuò)誤方式:使用 double 構(gòu)造方法

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

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

正確方式:使用字符串構(gòu)造器

BigDecimal bd = new BigDecimal("0.1");

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

BigDecimal bd = BigDecimal.valueOf(0.1);

該方法內(nèi)部先將 double 轉(zhuǎn)換為字符串再構(gòu)造,能避免部分精度問(wèn)題。

基本運(yùn)算(加減乘除)

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ù),舍入模式

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

精度控制與舍入模式

舍入模式說(shuō)明
RoundingMode.HALF_UP四舍五入(最常用)
RoundingMode.HALF_DOWN五舍六入
RoundingMode.UP遠(yuǎn)離零方向舍入
RoundingMode.DOWN向零方向舍入
RoundingMode.CEILING向正無(wú)窮方向舍入
RoundingMode.FLOOR向負(fù)無(wú)窮方向舍入
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 對(duì)象

錯(cuò)誤方式:使用 equalsequals 會(huì)比較精度(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(大于)。

五、最后總結(jié):3句話避開(kāi)浮點(diǎn)精度坑

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

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

相關(guān)文章

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

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

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

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

    本文詳細(xì)講解了Java正則表達(dá)式的用法,舉例演示電話和郵箱格式,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • 談?wù)勎覍?duì)Spring Bean 生命周期的理解

    談?wù)勎覍?duì)Spring Bean 生命周期的理解

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

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

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

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

    這篇文章主要介紹了idea2020.3.3集成maven的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • 面向?qū)ο缶幊?Java中的抽象數(shù)據(jù)類型

    面向?qū)ο缶幊?Java中的抽象數(shù)據(jù)類型

    面向?qū)ο缶幊?Java中的抽象數(shù)據(jù)類型...
    2006-12-12
  • Java數(shù)據(jù)結(jié)構(gòu)之平衡二叉樹(shù)的實(shí)現(xiàn)詳解

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

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

    Java集合List相關(guān)面試題整理大全

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

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

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

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

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

最新評(píng)論

隆尧县| 剑阁县| 武宁县| 尼勒克县| 乃东县| 平顺县| 阳新县| 信宜市| 门源| 房产| 鱼台县| 金坛市| 沾化县| 丘北县| 额敏县| 东港市| 原阳县| 永新县| 汝城县| 普兰店市| 宁强县| 新民市| 石首市| 安顺市| 乌拉特中旗| 郁南县| 赤城县| 专栏| 康马县| 福州市| 五河县| 会东县| 潮安县| 扬中市| 萝北县| 鸡东县| 尼勒克县| 莫力| 贵定县| 翁牛特旗| 保定市|