国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

詳解java中BigDecimal精度問(wèn)題

發(fā)布時(shí)間:2021-07-06 11:13 來(lái)源:腳本之家 閱讀:0 作者:雨點(diǎn)的名字 欄目: 開(kāi)發(fā)技術(shù)

目錄

    一、背景

    在實(shí)際開(kāi)發(fā)中,對于 不需要任何準確計算精度的屬性可以直接使用float或double,但是如果需要精確計算結果,則必須使用BigDecimal,例如價(jià)格、質(zhì)量。

    為什么這么說(shuō),主要有兩點(diǎn)

    1、double計算會(huì )有精度丟失問(wèn)題

    2、在除法運算時(shí),BigDecimal提供了豐富的取舍規則。(double雖然可以通過(guò)NumberFormat進(jìn)行四舍五入,但是NumberFormat是線(xiàn)程不安全的)

    對于精度問(wèn)題我們可以看下實(shí)際的例子

    public static void main(String[] args) {
        //正常 3.3
        System.out.println("加法結果:"+(1.1+2.2));
        //正常 -7.9
        System.out.println("減法結果:"+(2.2-10.1));
        //正常 2.42
        System.out.println("乘法結果:"+(1.1*2.2));
        //正常 0.44
        System.out.println("除法結果:"+(4.4/10));
    }

    實(shí)際控制臺輸出

    為什么會(huì )這樣

    在于我們的計算機是二進(jìn)制的。浮點(diǎn)數沒(méi)有辦法是用二進(jìn)制進(jìn)行精確表示。我們的CPU表示浮點(diǎn)數由兩個(gè)部分組成:指數和尾數,這樣的表示方法一般都會(huì )

    失去一定的精確度,有些浮點(diǎn)數運算也會(huì )產(chǎn)生一定的誤差。如:2.4的二進(jìn)制表示并非就是精確的2.4。反而最為接近的二進(jìn)制表示是 2.3999999999999999。

    浮點(diǎn)數的值實(shí)際上是由一個(gè)特定的數學(xué)公式計算得到的。

    二、BigDecimal構造函數

    1、四種構造函數

    BigDecimal(int)     //創(chuàng  )建一個(gè)具有參數所指定整數值的對象。
    BigDecimal(double)  //創(chuàng  )建一個(gè)具有參數所指定雙精度值的對象。
    BigDecimal(long)    //創(chuàng  )建一個(gè)具有參數所指定長(cháng)整數值的對象。
    BigDecimal(String)  //創(chuàng  )建一個(gè)具有參數所指定以字符串表示的數值的對象。

    這幾個(gè)都是常用的構造器,他們返回的對象都是BigDecimal對象。換而言之,將BigDecimal對象轉換為其他類(lèi)型的對象,我們通過(guò)以下幾種。

    toString()          //將BigDecimal對象的數值轉換成字符串。
    doubleValue()       //將BigDecimal對象中的值以雙精度數返回。
    floatValue()        //將BigDecimal對象中的值以單精度數返回。
    longValue()         //將BigDecimal對象中的值以長(cháng)整數返回。
    intValue()          //將BigDecimal對象中的值以整數返回。

    這里需要非常注意BigDecimal(double)的構造函數,也是會(huì )存在精度丟失的問(wèn)題,其它的不會(huì ),這里也可以舉例說(shuō)明

    public static void main(String[] args) {
        BigDecimal intDecimal = new BigDecimal(10);
        BigDecimal doubleDecimal = new BigDecimal(4.3);
        BigDecimal longDecimal = new BigDecimal(10L);
        BigDecimal stringDecimal = new BigDecimal("4.3");
        System.out.println("intDecimal=" + intDecimal);
        System.out.println("doubleDecimal=" + doubleDecimal);
        System.out.println("longDecimal=" + longDecimal);
        System.out.println("stringDecimal=" + stringDecimal);
    }

    控制臺實(shí)際輸出

    從圖中很明顯可以看出,對于double的構造函數是會(huì )存在精度丟失的可能的。

    2、為什么會(huì )出現這種情況

    這個(gè)在new BigDecimal(double)類(lèi)型的構造函數上的注解有解釋說(shuō)明。

    這個(gè)構造函數的結果可能有些不可預測。 可以假設在Java中寫(xiě)入new BigDecimal(0.1)創(chuàng )建一個(gè)BigDecimal ,它完全等于0.1(非標尺值為1,比例為1),但實(shí)際上等于

    0.1000000000000000055511151231257827021181583404541015625。 這是因為0.1不能像double (或者作為任何有限長(cháng)度的二進(jìn)制分數)精確地表示。

    因此,正在被傳遞給構造的值不是正好等于0.1。

    3、如何解決

    有兩種常用的解決辦法。

    1、是將double 通過(guò)Double.toString(double)先轉為String,然后放入BigDecimal的String構造函數中。

    2、不通過(guò)BigDecimal的構造函數,而是通過(guò)它的靜態(tài)方法BigDecimal.valueOf(double),也同樣不會(huì )丟失精度。

    示例

     public static void main(String[] args) {
        String string = Double.toString(4.3);
        BigDecimal stringBigDecimal = new BigDecimal(string);
        BigDecimal bigDecimal = BigDecimal.valueOf(4.3);
        System.out.println("stringBigDecimal = " + stringBigDecimal);
        System.out.println("bigDecimal = " + bigDecimal);
    }

    運行結果

    這樣也能保證,對與double而言,轉BigDecimal不會(huì )出現精度丟失的情況。

    三、常用方法

    1、常用方法

    示例

    public static void main(String[] args) {
        BigDecimal a = new BigDecimal("4.5");
        BigDecimal b = new BigDecimal("1.5");
        BigDecimal c = new BigDecimal("-10.5");
    
        BigDecimal add_result = a.add(b);
        BigDecimal subtract_result = a.subtract(b);
        BigDecimal multiply_result = a.multiply(b);
        BigDecimal divide_result = a.divide(b);
        BigDecimal remainder_result = a.remainder(b);
        BigDecimal max_result = a.max(b);
        BigDecimal min_result = a.min(b);
        BigDecimal abs_result = c.abs();
        BigDecimal negate_result = a.negate();
    
        System.out.println("4.5+1.5=" + add_result);
        System.out.println("4.5-1.5=" + subtract_result);
        System.out.println("4.5*1.5=" + multiply_result);
        System.out.println("4.5/1.5=" + divide_result);
        System.out.println("4.5/1.5余數=" + remainder_result);
        System.out.println("4.5和1.5最大數=" + max_result);
        System.out.println("4.5和1.5最小數=" + min_result);
        System.out.println("-10.5的絕對值=" + abs_result);
        System.out.println("4.5的相反數=" + negate_result);
    }

    4.5+1.5=6.0

    4.5-1.5=3.0

    4.5*1.5=6.75

    4.5/1.5=3

    4.5/1.5余數=0.0

    4.5和1.5最大數=4.5

    4.5和1.5最小數=1.5

    -10.5的絕對值=10.5

    4.5的相反數=-4.5

    這里把除法單獨再講一下,因為除法操作的時(shí)候會(huì )有除不盡的情況,,比如 3,5/3,這時(shí)會(huì )報錯java.lang.ArithmeticException: Non-terminating decimal expansion;

    no exact representable decimal result。所以這里要考慮除不盡的情況下,保留幾位小數,取舍規則。(除法如果可能存在除不進(jìn),那就用下面方法)

    BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) 第一參數表示除數,第二個(gè)參數表示小數點(diǎn)后保留位數,第三個(gè)參數表示取舍規則。

    2、取舍規則

    ROUND_UP          //不管保留數字后面是大是小(0除外)都會(huì )進(jìn)1
    ROUND_DOWN        //保留設置數字,后面所有直接去除
    ROUND_HALF_UP     //常用的四舍五入 
    ROUND_HALF_DOWN   //五舍六入
    ROUND_CEILING     //向正無(wú)窮方向舍入
    ROUND_FLOOR       //向負無(wú)窮方向舍入
    ROUND_HALF_EVEN   //向(距離)最近的一邊舍入,除非兩邊(的距離)是相等,如果是這樣,如果保留位數是奇數,使用ROUND_HALF_UP,如果是偶數,使用ROUND_HALF_DOWN
    ROUND_UNNECESSARY //計算結果是精確的,不需要舍入模式 

    注意 我們最常用的應該是 ROUND_HALF_UP(四舍五入)

    這里舉幾個(gè)常用的取舍規則

     public static void main(String[] args) {
    
        BigDecimal a = new BigDecimal("1.15");
        BigDecimal b = new BigDecimal("1");
    
        //不管保留數字后面是大是小(0除外)都會(huì )進(jìn)1 所以這里輸出為1.2
        BigDecimal divide_1 = a.divide(b,1,BigDecimal.ROUND_UP);
        //保留設置數字,后面所有直接去除         所以這里輸出為1.1
        BigDecimal divide_2 = a.divide(b,1,BigDecimal.ROUND_DOWN);
        //常用的四舍五入         所以這里輸出1.2
        BigDecimal divide_3 = a.divide(b,1,BigDecimal.ROUND_HALF_UP);
        //這個(gè)可以理解成五舍六入   所以這里輸出1.1
        BigDecimal divide_4 = a.divide(b,1,BigDecimal.ROUND_HALF_DOWN);
        //這里將1.15改成1.16
        BigDecimal c = new BigDecimal("1.16");
        //那么這里就符合六入了 所以輸出變?yōu)?.2
        BigDecimal divide_5 = c.divide(b,1,BigDecimal.ROUND_HALF_DOWN);
        System.out.println("divide_1 = " + divide_1);
        System.out.println("divide_2 = " + divide_2);
        System.out.println("divide_3 = " + divide_3);
        System.out.println("divide_4 = " + divide_4);
        System.out.println("divide_5 = " + divide_5);
    
    }

    運行結果

    divide_1 = 1.2

    divide_2 = 1.1

    divide_3 = 1.2

    divide_4 = 1.1

    divide_5 = 1.2

    四、格式化

    由于NumberFormat類(lèi)的format()方法可以使用BigDecimal對象作為其參數,可以利用BigDecimal對超出16位有效數字的貨幣值,百分值,以及一般數值進(jìn)行格式化控制。

    以利用BigDecimal對貨幣和百分比格式化為例。首先,創(chuàng )建BigDecimal對象,進(jìn)行BigDecimal的算術(shù)運算后,分別建立對貨幣和百分比格式化的引用,最后利用

    BigDecimal對象作為format()方法的參數,輸出其格式化的貨幣值和百分比。

    示例

    public static void main(String[] args) {
        //建立貨幣格式化引用
        NumberFormat currency = NumberFormat.getCurrencyInstance();
        //建立百分比格式化引用
        NumberFormat percent = NumberFormat.getPercentInstance();
        //百分比小數點(diǎn)最多3位
        percent.setMaximumFractionDigits(3);
        //取整
        NumberFormat integerInstance = NumberFormat.getIntegerInstance();
        ////金額
        BigDecimal loanAmount = new BigDecimal("188.555");
        ////利率
        BigDecimal interestRate = new BigDecimal("0.018555555");
        //沒(méi)有指定保留位數的情況下 默認保留2位
        System.out.println("金額: " + currency.format(loanAmount));
        //貨幣(百分比)格式化   指定默認的取舍規則是四舍五入
        System.out.println("利率: " + percent.format(interestRate));
        //取整還有點(diǎn)不一樣 188.555取整為189, 188.51也是189 但是189.5確實(shí)188,所以它不是真正意義上的四舍五入
        System.out.println("取整: " + integerInstance.format(loanAmount));
    }

    運行結果

    金額: ¥188.56利率: 1.856%取整: 189

    這里有幾點(diǎn)在說(shuō)明下

    1、格式化的時(shí)候沒(méi)有指定保留位數的情況下 默認保留2位。

    2、貨幣(百分比)格式化 指定默認的取舍規則是四舍五入。

    3、取整還有點(diǎn)不一樣 188.555取整為189, 188.51也是189 但是189.5確實(shí)188,所以它不是真正意義上的四舍五入。

    以上就是詳解java中BigDecimal精度問(wèn)題的詳細內容,更多關(guān)于java的資料請關(guān)注腳本之家其它相關(guān)文章!

    免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

    亚洲AV乱码一区二区三区林ゆな| 亚洲国产中文在线二区三区免| 无码三级AV电影在线观看| 99热精这里只有精品| 国产顶级绝伦理论电视剧有哪些| 亚洲中文无码永久免弗|