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

java BigDecimal精度丟失及常見問分析

 更新時間:2023年02月03日 11:39:21   作者:podongfeng  
這篇文章主要為大家介紹了java BigDecimal精度丟失及常見問分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

概述

作為JAVA程序員,應(yīng)該或多或少跟BigDecimal打過交道。JAVA在java.math包中提供的API類BigDecimal,用來對超過16位有效位的數(shù)進行精確的運算。

精度丟失

先從1個問題說起,看如下代碼

System.out.println(0.1 + 0.2);

最后打印出的結(jié)果是0.30000000000000004,而不是預(yù)期的0.3。

有經(jīng)驗的開發(fā)同學(xué)應(yīng)該一下子看出來這就是因為double丟失精度導(dǎo)致。更深層次的原因,是因為我們的計算機底層是二進制的,只有0和1,對于整數(shù)來說,從低到高的每1位代表了1、2、4、8、16...這樣的2的正次數(shù)冪,只要位數(shù)足夠,每個整數(shù)都可以分解成這樣的2的正次數(shù)冪組合,例如7D=111B,13D=1101B。但是到了小數(shù)這里,就會發(fā)現(xiàn)2的負(fù)次數(shù)冪值是0.5、0.25、0.125、0.0625這樣的值,但是并不是每個小數(shù)都可以分解成這樣的2的負(fù)次數(shù)冪組合,例如你無法精確湊出0.1。所以,double的0.1其實并不是精確的0.1,只是通過幾個2的負(fù)次數(shù)冪值湊的近似的0.1,所以會出現(xiàn)前面0.1 + 0.2 = 0.30000000000000004這樣的結(jié)果。

適用場景

雙精度浮點型變量double可以處理16位有效數(shù),但是某些場景下,即使已經(jīng)做到了16位有效位的數(shù)還是不夠,比如涉及金額計算,差一點就會導(dǎo)致賬目不平。

常用方法

加減乘除

既然BigDecimal主要用于數(shù)值計算,那么最基礎(chǔ)的方法就是加減乘除。BigDecimal沒有對應(yīng)的數(shù)值類的基本數(shù)據(jù)類型,所以不能直接使用+、-、*、/這樣的符號來進行計算,而要使用BigDecimal內(nèi)部的方法。

public BigDecimal add(BigDecimal augend)
public BigDecimal subtract(BigDecimal subtrahend)
public BigDecimal multiply(BigDecimal multiplicand)
public BigDecimal divide(BigDecimal divisor)

需要注意的是,BigDecimal是不可變的,所以,add、subtractmultiply、divide方法都是有返回值的,返回值是一個新的BigDecimal對象,原來的BigDecimal值并沒有變。

設(shè)置精度和舍入策略

可以通過setScale方法來設(shè)置精度和舍入策略。

public BigDecimal setScale(int newScale, RoundingMode roundingMode)

第1個參數(shù)newScale代表精度,即小數(shù)點后位數(shù);第2個參數(shù)roundingMode代表舍入策略,RoundingMode是一個枚舉,用來替代原來在BigDecimal定義的常量,原來在BigDecimal定義的常量已經(jīng)標(biāo)記為Deprecated。在RoundingMode類中也通過1個valueOf方法來給出映射關(guān)系

/**
 * Returns the {@code RoundingMode} object corresponding to a
 * legacy integer rounding mode constant in {@link BigDecimal}.
 *
 * @param  rm legacy integer rounding mode to convert
 * @return {@code RoundingMode} corresponding to the given integer.
 * @throws IllegalArgumentException integer is out of range
 */
public static RoundingMode valueOf(int rm) {
    return switch (rm) {
        case BigDecimal.ROUND_UP          -> UP;
        case BigDecimal.ROUND_DOWN        -> DOWN;
        case BigDecimal.ROUND_CEILING     -> CEILING;
        case BigDecimal.ROUND_FLOOR       -> FLOOR;
        case BigDecimal.ROUND_HALF_UP     -> HALF_UP;
        case BigDecimal.ROUND_HALF_DOWN   -> HALF_DOWN;
        case BigDecimal.ROUND_HALF_EVEN   -> HALF_EVEN;
        case BigDecimal.ROUND_UNNECESSARY -> UNNECESSARY;
        default -> throw new IllegalArgumentException("argument out of range");
    };
}

我們逐一看一下每個值的含義

  • UP
    直接進位,例如下面代碼結(jié)果是3.15
BigDecimal pi = BigDecimal.valueOf(3.141);
System.out.println(pi.setScale(2, RoundingMode.UP));
  • DOWN
    直接舍去,例如下面代碼結(jié)果是3.1415
BigDecimal pi = BigDecimal.valueOf(3.14159);
System.out.println(pi.setScale(4, RoundingMode.DOWN));
  • CEILING
    如果是正數(shù),相當(dāng)于UP;如果是負(fù)數(shù),相當(dāng)于DOWN。
  • FLOOR
    如果是正數(shù),相當(dāng)于DOWN;如果是負(fù)數(shù),相當(dāng)于UP。
  • HALF_UP
    就是我們正常理解的四舍五入,實際上應(yīng)該也是最常用的。 下面的代碼結(jié)果是3.14
BigDecimal pi = BigDecimal.valueOf(3.14159);
System.out.println(pi.setScale(2, RoundingMode.HALF_UP));

下面的代碼結(jié)果是3.142

BigDecimal pi = BigDecimal.valueOf(3.14159);
System.out.println(pi.setScale(3, RoundingMode.HALF_UP));
  • HALF_DOWN
    與四舍五入類似,這種是五舍六入。我們對于HALF_UP和HALF_DOWN可以理解成對于5的處理不同,UP遇到5是進位處理,DOWN遇到5是舍去處理,
  • HALF_EVEN
    如果舍棄部分左邊的數(shù)字為偶數(shù),相當(dāng)于HALF_DOWN;如果舍棄部分左邊的數(shù)字為奇數(shù),相當(dāng)于HALF_UP
  • UNNECESSARY
    非必要舍入。如果除去小數(shù)的后導(dǎo)0后,位數(shù)小于等于scale,那么就是去除scale位數(shù)后面的后導(dǎo)0;位數(shù)大于scale,拋出ArithmeticException。
    下面代碼結(jié)果是3.14
BigDecimal pi = BigDecimal.valueOf(3.1400);
System.out.println(pi.setScale(2, RoundingMode.UNNECESSARY));

下面代碼拋出ArithmeticException

BigDecimal pi = BigDecimal.valueOf(3.1400);
System.out.println(pi.setScale(1, RoundingMode.UNNECESSARY));

常見問題

創(chuàng)建BigDecimal對象

先看下面代碼

BigDecimal a = new BigDecimal(0.1);
System.out.println(a);

實際輸出的結(jié)果是0.1000000000000000055511151231257827021181583404541015625。其實這跟我們開篇引出的精度丟失是同一個問題,這里構(gòu)造方法中的參數(shù)0.1是double類型,本身無法精確表示0.1,雖然BigDecimal并不會導(dǎo)致精度丟失,但是在更加上游的源頭,double類型的0.1已經(jīng)丟失了精度,這里用一個已經(jīng)丟失精度的0.1來創(chuàng)建不會丟失精度的BigDecimal,精度還是會丟失。類似于使用2K的清晰度重新錄制了一遍原始只有360P的視頻,清晰度也不會優(yōu)于原始的360P。
所以,我們應(yīng)該盡量避免使用double來創(chuàng)建BigDecimal,確實源頭是double的,我們可以使用valueOf方法,這個方法會先調(diào)用Double.toString(val)來轉(zhuǎn)成String,這樣就不會產(chǎn)生精度丟失,下面的代碼結(jié)果就是0.1

BigDecimal a = BigDecimal.valueOf(0.1);
System.out.println(a);

順便說一下,BigDecimal還內(nèi)置了ZERO、ONE、TEN這樣的常量可以直接使用。

toString

這個問題比較隱蔽,在數(shù)據(jù)比較小的時候不會遇到,但是看如下代碼

BigDecimal a = BigDecimal.valueOf(987654321987654321.123456789123456789);
System.out.println(a);

最后實際輸出的結(jié)果是9.8765432198765427E+17。原因是System.out.println會自動調(diào)用BigDecimal的toString方法,而這個方法會在必要時使用科學(xué)計數(shù)法,如果不想使用科學(xué)計數(shù)法,可以使用BigDecimal的toPlainString方法。另外提一下,BigDecimal還提供了一個toEngineeringString方法,這個方法也會使用科學(xué)技術(shù)法,不一樣的是,這里面的10都是3、6、9這樣的冪,對應(yīng)我們在查看大數(shù)的時候,很多都是每3位會增加1個逗號。

comparTo 和 equals

這個問題出現(xiàn)的不多,有經(jīng)驗的開發(fā)同學(xué)在比較數(shù)值的時候,會自然而然使用comparTo方法。這里說一下BigDecimal的equals方法除了比較數(shù)值之外,還會比較scale精度,不同精度不會equles。
例如下面代碼分別會返回0false

BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.10");
System.out.println(a.compareTo(b));
System.out.println(a.equals(b));

不能除盡時ArithmeticException異常

上面提到的加減乘除的4個方法中,除法會比較特殊,因為可能出現(xiàn)除不盡的情況,這時如果沒有設(shè)置精度,就會拋出ArithmeticException,因為這個是否能除盡是跟具體數(shù)值相關(guān)的,這會導(dǎo)致偶現(xiàn)的bug,更加難以排查。
例如下面代碼就會拋出ArithmeticException異常

BigDecimal a = new BigDecimal(1);
BigDecimal b = new BigDecimal(3);
System.out.println(a.divide(b));

應(yīng)對的方法是,在除法運算時,注意設(shè)置結(jié)果的精度和舍入模式,下面的代碼就能正常輸出結(jié)果0.33

BigDecimal a = new BigDecimal(1);
BigDecimal b = new BigDecimal(3);
System.out.println(a.divide(b, 2, RoundingMode.HALF_UP));

總結(jié)

BigDecimal主要用于double因為精度丟失而不滿足的某些特殊業(yè)務(wù)場景,例如會計金額計算。在可以忍受略微不精確的場景還是使用內(nèi)部提供的add、subtract、multiply、divide方法來進行基礎(chǔ)的加減乘除運算,運算后會返回新的對象,原始的對象并不會改變。在使用BigDecimal的過程中,要注意創(chuàng)建對象、toString、比較數(shù)值、不能除盡時需要設(shè)置精度等問題。

以上就是java BigDecimal精度丟失及常見問分析的詳細(xì)內(nèi)容,更多關(guān)于java BigDecimal精度丟失的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

山西省| 三门峡市| 屯留县| 花莲市| 商洛市| 泌阳县| 潮安县| 明光市| 安岳县| 石家庄市| 四会市| 西乌珠穆沁旗| 云南省| 汉中市| 阿巴嘎旗| 朝阳市| 巴楚县| 哈尔滨市| 仪征市| 塘沽区| 修文县| 乌什县| 涪陵区| 苗栗县| 陇南市| 佳木斯市| 威海市| 班戈县| 永丰县| 海南省| 田阳县| 奈曼旗| 始兴县| 郸城县| 南康市| 腾冲县| 仁布县| 田东县| 濮阳市| 彭州市| 余姚市|