您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關(guān)java中BigDecimal精度問題的示例分析的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
在實際開發(fā)中,對于 不需要任何準確計算精度的屬性可以直接使用float或double,但是如果需要精確計算結(jié)果,則必須使用BigDecimal,例如價格、質(zhì)量。
為什么這么說,主要有兩點
1、double計算會有精度丟失問題
2、在除法運算時,BigDecimal提供了豐富的取舍規(guī)則。(double雖然可以通過NumberFormat進行四舍五入,但是NumberFormat是線程不安全的)
對于精度問題我們可以看下實際的例子
public static void main(String[] args) { //正常 3.3 System.out.println("加法結(jié)果:"+(1.1+2.2)); //正常 -7.9 System.out.println("減法結(jié)果:"+(2.2-10.1)); //正常 2.42 System.out.println("乘法結(jié)果:"+(1.1*2.2)); //正常 0.44 System.out.println("除法結(jié)果:"+(4.4/10)); }
實際控制臺輸出
為什么會這樣
在于我們的計算機是二進制的。浮點數(shù)沒有辦法是用二進制進行精確表示。我們的CPU表示浮點數(shù)由兩個部分組成:指數(shù)和尾數(shù),這樣的表示方法一般都會
失去一定的精確度,有些浮點數(shù)運算也會產(chǎn)生一定的誤差。如:2.4的二進制表示并非就是精確的2.4。反而最為接近的二進制表示是 2.3999999999999999。
浮點數(shù)的值實際上是由一個特定的數(shù)學公式計算得到的。
BigDecimal(int) //創(chuàng)建一個具有參數(shù)所指定整數(shù)值的對象。 BigDecimal(double) //創(chuàng)建一個具有參數(shù)所指定雙精度值的對象。 BigDecimal(long) //創(chuàng)建一個具有參數(shù)所指定長整數(shù)值的對象。 BigDecimal(String) //創(chuàng)建一個具有參數(shù)所指定以字符串表示的數(shù)值的對象。
這幾個都是常用的構(gòu)造器,他們返回的對象都是BigDecimal對象。換而言之,將BigDecimal對象轉(zhuǎn)換為其他類型的對象,我們通過以下幾種。
toString() //將BigDecimal對象的數(shù)值轉(zhuǎn)換成字符串。 doubleValue() //將BigDecimal對象中的值以雙精度數(shù)返回。 floatValue() //將BigDecimal對象中的值以單精度數(shù)返回。 longValue() //將BigDecimal對象中的值以長整數(shù)返回。 intValue() //將BigDecimal對象中的值以整數(shù)返回。
這里需要非常注意BigDecimal(double)的構(gòu)造函數(shù),也是會存在精度丟失的問題,其它的不會,這里也可以舉例說明
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); }
控制臺實際輸出
從圖中很明顯可以看出,對于double的構(gòu)造函數(shù)是會存在精度丟失的可能的。
這個在new BigDecimal(double)類型的構(gòu)造函數(shù)上的注解有解釋說明。
這個構(gòu)造函數(shù)的結(jié)果可能有些不可預測。 可以假設(shè)在Java中寫入new BigDecimal(0.1)創(chuàng)建一個BigDecimal ,它完全等于0.1(非標尺值為1,比例為1),但實際上等于
0.1000000000000000055511151231257827021181583404541015625。 這是因為0.1不能像double (或者作為任何有限長度的二進制分數(shù))精確地表示。
因此,正在被傳遞給構(gòu)造的值不是正好等于0.1。
有兩種常用的解決辦法。
1、是將double 通過Double.toString(double)先轉(zhuǎn)為String,然后放入BigDecimal的String構(gòu)造函數(shù)中。
2、不通過BigDecimal的構(gòu)造函數(shù),而是通過它的靜態(tài)方法BigDecimal.valueOf(double),也同樣不會丟失精度。
示例
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); }
運行結(jié)果
這樣也能保證,對與double而言,轉(zhuǎn)BigDecimal不會出現(xiàn)精度丟失的情況。
示例
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余數(shù)=" + remainder_result); System.out.println("4.5和1.5最大數(shù)=" + max_result); System.out.println("4.5和1.5最小數(shù)=" + min_result); System.out.println("-10.5的絕對值=" + abs_result); System.out.println("4.5的相反數(shù)=" + 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余數(shù)=0.0
4.5和1.5最大數(shù)=4.5
4.5和1.5最小數(shù)=1.5
-10.5的絕對值=10.5
4.5的相反數(shù)=-4.5
這里把除法單獨再講一下,因為除法操作的時候會有除不盡的情況,,比如 3,5/3,這時會報錯java.lang.ArithmeticException: Non-terminating decimal expansion;
no exact representable decimal result。所以這里要考慮除不盡的情況下,保留幾位小數(shù),取舍規(guī)則。(除法如果可能存在除不進,那就用下面方法)
BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) 第一參數(shù)表示除數(shù),第二個參數(shù)表示小數(shù)點后保留位數(shù),第三個參數(shù)表示取舍規(guī)則。
ROUND_UP //不管保留數(shù)字后面是大是小(0除外)都會進1 ROUND_DOWN //保留設(shè)置數(shù)字,后面所有直接去除 ROUND_HALF_UP //常用的四舍五入 ROUND_HALF_DOWN //五舍六入 ROUND_CEILING //向正無窮方向舍入 ROUND_FLOOR //向負無窮方向舍入 ROUND_HALF_EVEN //向(距離)最近的一邊舍入,除非兩邊(的距離)是相等,如果是這樣,如果保留位數(shù)是奇數(shù),使用ROUND_HALF_UP,如果是偶數(shù),使用ROUND_HALF_DOWN ROUND_UNNECESSARY //計算結(jié)果是精確的,不需要舍入模式
注意 我們最常用的應該是 ROUND_HALF_UP(四舍五入)
這里舉幾個常用的取舍規(guī)則
public static void main(String[] args) { BigDecimal a = new BigDecimal("1.15"); BigDecimal b = new BigDecimal("1"); //不管保留數(shù)字后面是大是小(0除外)都會進1 所以這里輸出為1.2 BigDecimal divide_1 = a.divide(b,1,BigDecimal.ROUND_UP); //保留設(shè)置數(shù)字,后面所有直接去除 所以這里輸出為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); //這個可以理解成五舍六入 所以這里輸出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); }
運行結(jié)果
divide_1 = 1.2
divide_2 = 1.1
divide_3 = 1.2
divide_4 = 1.1
divide_5 = 1.2
由于NumberFormat類的format()方法可以使用BigDecimal對象作為其參數(shù),可以利用BigDecimal對超出16位有效數(shù)字的貨幣值,百分值,以及一般數(shù)值進行格式化控制。
以利用BigDecimal對貨幣和百分比格式化為例。首先,創(chuàng)建BigDecimal對象,進行BigDecimal的算術(shù)運算后,分別建立對貨幣和百分比格式化的引用,最后利用
BigDecimal對象作為format()方法的參數(shù),輸出其格式化的貨幣值和百分比。
示例
public static void main(String[] args) { //建立貨幣格式化引用 NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立百分比格式化引用 NumberFormat percent = NumberFormat.getPercentInstance(); //百分比小數(shù)點最多3位 percent.setMaximumFractionDigits(3); //取整 NumberFormat integerInstance = NumberFormat.getIntegerInstance(); ////金額 BigDecimal loanAmount = new BigDecimal("188.555"); ////利率 BigDecimal interestRate = new BigDecimal("0.018555555"); //沒有指定保留位數(shù)的情況下 默認保留2位 System.out.println("金額: " + currency.format(loanAmount)); //貨幣(百分比)格式化 指定默認的取舍規(guī)則是四舍五入 System.out.println("利率: " + percent.format(interestRate)); //取整還有點不一樣 188.555取整為189, 188.51也是189 但是189.5確實188,所以它不是真正意義上的四舍五入 System.out.println("取整: " + integerInstance.format(loanAmount)); }
運行結(jié)果
金額: ¥188.56利率: 1.856%取整: 189
這里有幾點在說明下
1、格式化的時候沒有指定保留位數(shù)的情況下 默認保留2位。
2、貨幣(百分比)格式化 指定默認的取舍規(guī)則是四舍五入。
3、取整還有點不一樣 188.555取整為189, 188.51也是189 但是189.5確實188,所以它不是真正意義上的四舍五入。
Java中的集合主要分為四類:1、List列表:有序的,可重復的;2、Queue隊列:有序,可重復的;3、Set集合:不可重復;4、Map映射:無序,鍵唯一,值不唯一。
感謝各位的閱讀!關(guān)于“java中BigDecimal精度問題的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。