溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java的BigDecimal類(lèi)怎么用

發(fā)布時(shí)間:2022-01-06 20:02:19 來(lái)源:億速云 閱讀:137 作者:iii 欄目:編程語(yǔ)言

這篇文章主要介紹“Java的BigDecimal類(lèi)怎么用”,在日常操作中,相信很多人在Java的BigDecimal類(lèi)怎么用問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Java的BigDecimal類(lèi)怎么用”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

如果我們編譯運(yùn)行下面這個(gè)程序會(huì)看到什么?

public class Test{     public static void main(String args[]){         System.out.println(0.05+0.01);         System.out.println(1.0-0.42);         System.out.println(4.015*100);         System.out.println(123.3/100);     } }

你沒(méi)有看錯(cuò)!結(jié)果確實(shí)是:

0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999

Java中的簡(jiǎn)單浮點(diǎn)數(shù)類(lèi)型float和double不能夠進(jìn)行運(yùn)算。不光是Java,在其它很多編程語(yǔ)言中也有這樣的問(wèn)題。在大多數(shù)情況下,計(jì)算的結(jié)果是準(zhǔn)確的,但是多試幾次(可以做一個(gè)循環(huán))就可以試出類(lèi)似上面的錯(cuò)誤。現(xiàn)在終于理解為什么要有BCD碼了。

這個(gè)問(wèn)題相當(dāng)嚴(yán)重,如果你有9.999999999999元,你的計(jì)算機(jī)是不會(huì)認(rèn)為你可以購(gòu)買(mǎi)10元的商品的。

在有的編程語(yǔ)言中提供了專(zhuān)門(mén)的貨幣類(lèi)型來(lái)處理這種情況,但是Java沒(méi)有?,F(xiàn)在讓我們看看如何解決這個(gè)問(wèn)題。

四舍五入

我們的***個(gè)反應(yīng)是做四舍五入。Math類(lèi)中的round方法不能設(shè)置保留幾位小數(shù),我們只能象這樣(保留兩位):

public double round(double value){     return Math.round(value*100)/100.0; }

非常不幸,上面的代碼并不能正常工作,給這個(gè)方法傳入4.015它將返回4.01而不是4.02,如我們?cè)谏厦婵吹降?/p>

4.015*100=401.49999999999994

因此如果我們要做到精確的四舍五入,不能利用簡(jiǎn)單類(lèi)型做任何運(yùn)算

java.text.DecimalFormat也不能解決這個(gè)問(wèn)題:

System.out.println(new java.text.DecimalFormat("0.00").format(4.025));

輸出是4.02

BigDecimal

在《Effective Java》這本書(shū)中也提到這個(gè)原則,float和double只能用來(lái)做科學(xué)計(jì)算或者是工程計(jì)算,在商業(yè)計(jì)算中我們要用java.math.BigDecimal。BigDecimal一共有4個(gè)構(gòu)造方法,我們不關(guān)心用BigInteger來(lái)構(gòu)造的那兩個(gè),那么還有兩個(gè),它們是:

BigDecimal(double val)
Translates a double into a BigDecimal.
BigDecimal(String val)
Translates the String representation of a BigDecimal into a BigDecimal.

上面的API簡(jiǎn)要描述相當(dāng)?shù)拿鞔_,而且通常情況下,上面的那一個(gè)使用起來(lái)要方便一些。我們可能想都不想就用上了,會(huì)有什么問(wèn)題呢?等到出了問(wèn)題的時(shí)候,才發(fā)現(xiàn)上面哪個(gè)構(gòu)造方法的詳細(xì)說(shuō)明中有這么一段:

Note: the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to .1000000000000000055511151231257827021181583404541015625. This is so because .1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the long value that is being passed in to the constructor is not exactly equal to .1, appearances nonwithstanding.
The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal(".1") is exactly equal to .1, as one would expect. Therefore, it is generally recommended that the (String) constructor be used in preference to this one.

原來(lái)我們?nèi)绻枰_計(jì)算,非要用String來(lái)構(gòu)造BigDecimal不可!在《Effective Java》一書(shū)中的例子是用String來(lái)構(gòu)造BigDecimal的,但是書(shū)上卻沒(méi)有強(qiáng)調(diào)這一點(diǎn)。

解決方案

現(xiàn)在我們已經(jīng)可以解決這個(gè)問(wèn)題了,原則是使用BigDecimal并且一定要用String來(lái)構(gòu)造。

但是想像一下吧,如果我們要做一個(gè)加法運(yùn)算,需要先將兩個(gè)浮點(diǎn)數(shù)轉(zhuǎn)為String,然后構(gòu)造成BigDecimal,在其中一個(gè)上調(diào)用add方法,傳入另一個(gè)作為參數(shù),然后把運(yùn)算的結(jié)果(BigDecimal)再轉(zhuǎn)換為浮點(diǎn)數(shù)。你能夠忍受這么煩瑣的過(guò)程嗎?下面我們提供一個(gè)工具類(lèi)Arith來(lái)簡(jiǎn)化操作。它提供以下靜態(tài)方法,包括加減乘除和四舍五入:

public static double add(double v1,double v2) public static double sub(double v1,double v2) public static double mul(double v1,double v2) public static double div(double v1,double v2) public static double div(double v1,double v2,int scale) public static double round(double v,int scale)

附  錄

源文件Arith.java:

import java.math.BigDecimal; /** * 由于Java的簡(jiǎn)單類(lèi)型不能夠精確的對(duì)浮點(diǎn)數(shù)進(jìn)行運(yùn)算,這個(gè)工具類(lèi)提供精 * 確的浮點(diǎn)數(shù)運(yùn)算,包括加減乘除和四舍五入。 */ public class Arith{     //默認(rèn)除法運(yùn)算精度     private static final int DEF_DIV_SCALE = 10;     //這個(gè)類(lèi)不能實(shí)例化     private Arith(){     }      /**      * 提供精確的加法運(yùn)算。      * @param v1 被加數(shù)      * @param v2 加數(shù)      * @return 兩個(gè)參數(shù)的和      */     public static double add(double v1,double v2){         BigDecimal b1 = new BigDecimal(Double.toString(v1));         BigDecimal b2 = new BigDecimal(Double.toString(v2));         return b1.add(b2).doubleValue();     }     /**      * 提供精確的減法運(yùn)算。      * @param v1 被減數(shù)      * @param v2 減數(shù)      * @return 兩個(gè)參數(shù)的差      */     public static double sub(double v1,double v2){         BigDecimal b1 = new BigDecimal(Double.toString(v1));         BigDecimal b2 = new BigDecimal(Double.toString(v2));         return b1.subtract(b2).doubleValue();     }      /**      * 提供精確的乘法運(yùn)算。      * @param v1 被乘數(shù)      * @param v2 乘數(shù)      * @return 兩個(gè)參數(shù)的積      */     public static double mul(double v1,double v2){         BigDecimal b1 = new BigDecimal(Double.toString(v1));         BigDecimal b2 = new BigDecimal(Double.toString(v2));         return b1.multiply(b2).doubleValue();     }      /**      * 提供(相對(duì))精確的除法運(yùn)算,當(dāng)發(fā)生除不盡的情況時(shí),精確到      * 小數(shù)點(diǎn)以后10位,以后的數(shù)字四舍五入。      * @param v1 被除數(shù)      * @param v2 除數(shù)      * @return 兩個(gè)參數(shù)的商      */     public static double div(double v1,double v2){         return div(v1,v2,DEF_DIV_SCALE);     }      /**      * 提供(相對(duì))精確的除法運(yùn)算。當(dāng)發(fā)生除不盡的情況時(shí),由scale參數(shù)指      * 定精度,以后的數(shù)字四舍五入。      * @param v1 被除數(shù)      * @param v2 除數(shù)      * @param scale 表示表示需要精確到小數(shù)點(diǎn)以后幾位。      * @return 兩個(gè)參數(shù)的商      */     public static double div(double v1,double v2,int scale){         if(scale<0){             throw new IllegalArgumentException(                 "The scale must be a positive integer or zero");         }         BigDecimal b1 = new BigDecimal(Double.toString(v1));         BigDecimal b2 = new BigDecimal(Double.toString(v2));         return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();     }      /**      * 提供精確的小數(shù)位四舍五入處理。      * @param v 需要四舍五入的數(shù)字      * @param scale 小數(shù)點(diǎn)后保留幾位      * @return 四舍五入后的結(jié)果      */     public static double round(double v,int scale){         if(scale<0){             throw new IllegalArgumentException(                 "The scale must be a positive integer or zero");         }         BigDecimal b = new BigDecimal(Double.toString(v));         BigDecimal one = new BigDecimal("1");         return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();     } };

***我們利用BigDecimal提供的精確計(jì)算來(lái)對(duì)最開(kāi)始提到的例子進(jìn)行測(cè)試 :

public class Test {         public static void main(String[] args) {               //直接使用浮點(diǎn)數(shù)進(jìn)行計(jì)算,得到的結(jié)果是有問(wèn)題的               System.out.println(0.01+0.05);               //使用了BigDecimal類(lèi)進(jìn)行計(jì)算后,可以做到精確計(jì)算               System.out.println(Arith.add(0.05, 0.01));        } }

控制臺(tái)輸出:

0.060000000000000005
0.06

BigDecimal精度計(jì)算的舍入模式

ROUND_CEILING. 類(lèi) java.math.BigDecimal 中的靜態(tài)變量:如果 BigDecimal 為正,則作 ROUND_UP 操作;如果為負(fù),則作 ROUND_DOWN 操作。

ROUND_DOWN. 類(lèi) java.math.BigDecimal 中的靜態(tài)變量:從不在舍棄(即截?cái)?的小數(shù)之前增加數(shù)字。

ROUND_FLOOR. 類(lèi) java.math.BigDecimal 中的靜態(tài)變量:如果 BigDecimal 為正,則作 ROUND_UP ;如果為負(fù),則作 ROUND_DOWN 。

ROUND_HALF_DOWN. 類(lèi) java.math.BigDecimal 中的靜態(tài)變量:若舍棄部分> .5 ,則作 ROUND_DOWN;否則,作 ROUND_DOWN 操作。

ROUND_HALF_EVEN. 類(lèi) java.math.BigDecimal 中的靜態(tài)變量:如果舍棄部分左邊的數(shù)字為奇數(shù),則作 ROUND_HALF_UP 操作;如果它為偶數(shù),則作 ROUND_HALF_DOWN 操作。

ROUND_HALF_UP. 類(lèi) java.math.BigDecimal 中的靜態(tài)變量:若舍棄部分>=.5,則作 ROUND_UP ;否則,則作 ROUND_DOWN。

ROUND_UNNECESSARY. 類(lèi) java.math.BigDecimal 中的靜態(tài)變量:該 “偽舍入模式”實(shí)際是要求操作有一個(gè)精確結(jié)果,,因此不需要舍入。

ROUND_UP. 類(lèi) java.math.BigDecimal 中的靜態(tài)變量:總是在非零的舍棄小數(shù)(即截?cái)?之前增加數(shù)字。

到此,關(guān)于“Java的BigDecimal類(lèi)怎么用”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI