Java中double精度丟失問題原因及解決辦法
double類型精度丟失問題:
0.1*0.1使用計算器計算是0.01,代碼里卻是0.010000000000000002
public class HelloWorld { public static void main(String []args) { double number1 = 0.1; double number2 = 0.1; double result = number1 * number2 ; System.out.println("使用double運算結(jié)果: "+result); } }
為什么會這樣呢?這就是精度丟失問題造成的。
為什么會出現(xiàn)精度丟失?
因為計算機只能識別0和1,即二進制,無論哪種編程語言,都需要翻譯成二進制才能被計算機識別。
很多人還知道這樣一句話:這種舍入誤差的主要原因是浮點數(shù)值采用二進制系統(tǒng)表示, 而在二進制系統(tǒng)中無法精確地表示分數(shù) 1/10。這就好像十進制無法精確地表示分數(shù) 1/3—樣。
針對十進制,1除以3是除不盡的。很好理解,因為我們一直接觸的就是十進制,等于0.333333… 很好理解
但是:二進制系統(tǒng)中無法精確地表示分數(shù) 1/10。為啥呢。就有點不理解了
《Java核心技術(shù)卷》書上也是這么寫的。
十進制 轉(zhuǎn)二進制(每次將小數(shù)部分乘2,取出整數(shù)部分,如果小數(shù)部分為0,就可以停止這個過程):十進制0.1
0.1*2=0.2 0.2*2=0.4 0.4*2=0.8 0.8*2=1.6 0.6*2=1.2 0.2*2=0.4 0.4*2=0.8 //... 應該已經(jīng)發(fā)現(xiàn),上面的過程已經(jīng)開始循環(huán),小數(shù)部分永遠不能為0
怎么解決精度丟失問題?
在商城里面計算訂單金額的時候,我們就不得不解決這個問題了,這時候就用到了BigDecimal
BigDecimal類位于java.math包下,用于對超過16位有效位的數(shù)進行精確的運算。
一般來說,double類型的變量可以處理16位有效數(shù),
但實際應用中,如果超過16位,就需要BigDecimal類來操作
new BigDecimal(double val)
new BigDecimal(String val)
BigDecimal.valueOf(double val)
將double轉(zhuǎn)為BigDecimal的時候,需要先把double轉(zhuǎn)換為字符串,然后再作為BigDecimal(String val)構(gòu)造函數(shù)的參數(shù),這樣才能避免出現(xiàn)精度問題。
import java.math.BigDecimal; public class BigDecimalUtil { /** * double類型的加法運算(不需要舍入) * @param d1 * @param d2 * @return 不加doubleValue()則, 返回BigDecimal對象 */ public static double addDouble(double d1, double d2) { BigDecimal p1 = new BigDecimal(Double.toString(d1)); BigDecimal p2 = new BigDecimal(Double.toString(d2)); return p1.add(p2).doubleValue(); } /** * double類型的加法運算(需要舍入) * @param d1 * @param d2 * @param scale 保留scale位小數(shù) * @return 不加doubleValue()則, 返回BigDecimal對象 */ public static double addDouble(double d1, double d2, int scale) { BigDecimal p1 = new BigDecimal(Double.toString(d1)); BigDecimal p2 = new BigDecimal(Double.toString(d2)); return p1.add(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * double類型的超大數(shù)值加法運算(超過50 0000)(需要舍入) * @param d1 * @param d2 * @param scale 保留scale位小數(shù) * @return 返回字符串,不然double數(shù)字會轉(zhuǎn)成科學計數(shù)法顯示 */ public static String addDoubleToStr(double d1, double d2, int scale) { BigDecimal p1 = new BigDecimal(Double.toString(d1)); BigDecimal p2 = new BigDecimal(Double.toString(d2)); return p1.add(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); } /** * double類型的減法運算 * @param d1 * @param d2 * @return 不加doubleValue()則, 返回BigDecimal對象 */ public static double subtractDouble(double d1, double d2) { BigDecimal p1 = new BigDecimal(Double.toString(d1)); BigDecimal p2 = new BigDecimal(Double.toString(d2)); return p1.subtract(p2).doubleValue(); } /** * double類型的減法運算(需要舍入) * @param d1 * @param d2 * @param scale 保留scale位小數(shù) * @return 不加doubleValue()則, 返回BigDecimal對象 */ public static double subtractDouble(double d1, double d2, int scale) { BigDecimal p1 = new BigDecimal(Double.toString(d1)); BigDecimal p2 = new BigDecimal(Double.toString(d2)); return p1.subtract(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * double類型的超大數(shù)值減法運算(需要舍入) * @param d1 * @param d2 * @param scale 保留scale位小數(shù) * @return 返回字符串,不然double數(shù)字會轉(zhuǎn)成科學計數(shù)法顯示 */ public static String subtractDoubleToStr(double d1, double d2, int scale) { BigDecimal p1 = new BigDecimal(Double.toString(d1)); BigDecimal p2 = new BigDecimal(Double.toString(d2)); return p1.subtract(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); } /** * double類型的乘法運算 * * @param d1 * @param d2 * @return 不加doubleValue()則, 返回BigDecimal對象 */ public static double multiplyDouble(double d1, double d2) { BigDecimal p1 = new BigDecimal(Double.toString(d1)); BigDecimal p2 = new BigDecimal(Double.toString(d2)); return p1.multiply(p2).doubleValue(); } /** * double類型的乘法運算(需要舍入) * @param d1 * @param d2 * @param scale 保留scale位小數(shù) * @return 不加doubleValue()則, 返回BigDecimal對象 */ public static double multiplyDouble(double d1, double d2, int scale) { BigDecimal p1 = new BigDecimal(Double.toString(d1)); BigDecimal p2 = new BigDecimal(Double.toString(d2)); return p1.multiply(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * double類型的超大數(shù)值的乘法運算(需要舍入) * @param d1 * @param d2 * @param scale 保留scale位小數(shù) * @return 返回字符串,不然double數(shù)字會轉(zhuǎn)成科學計數(shù)法顯示 */ public static String multiplyDoubleToStr(double d1, double d2, int scale) { BigDecimal p1 = new BigDecimal(Double.toString(d1)); BigDecimal p2 = new BigDecimal(Double.toString(d2)); return p1.multiply(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString(); } /** * double類型的除法運算(需要舍入) * * @param d1 * @param d2 * @param scale 保留scale位小數(shù) * @return 不加doubleValue()則, 返回BigDecimal對象 */ public static double divideDouble(double d1, double d2, int scale) { if (scale < 0) { throw new IllegalArgumentException("Parameter error"); } BigDecimal p1 = new BigDecimal(Double.toString(d1)); BigDecimal p2 = new BigDecimal(Double.toString(d2)); return p1.divide(p2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * double類型的超大數(shù)值的除法運算(需要舍入) * @param d1 * @param d2 * @param scale 保留scale位小數(shù) * @return 返回字符串,不然double數(shù)字會轉(zhuǎn)成科學計數(shù)法顯示 */ public static String divideDoubleToStr(double d1, double d2, int scale) { if (scale < 0) { throw new IllegalArgumentException("Parameter error"); } BigDecimal p1 = new BigDecimal(Double.toString(d1)); BigDecimal p2 = new BigDecimal(Double.toString(d2)); return p1.divide(p2, scale, BigDecimal.ROUND_HALF_UP).toPlainString(); } }
各個roundingMode詳解如下:
- ROUND_UP:非0時,舍棄小數(shù)后(整數(shù)部分)加1,比如12.49結(jié)果為13,-12.49結(jié)果為 -13
- ROUND_DOWN:直接舍棄小數(shù)
- ROUND_CEILING:如果 BigDecimal 是正的,則做 ROUND_UP 操作;如果為負,則做 ROUND_DOWN 操作 (一句話:取附近較大的整數(shù))
- ROUND_FLOOR: 如果 BigDecimal 是正的,則做 ROUND_DOWN 操作;如果為負,則做 ROUND_UP 操作(一句話:取附近較小的整數(shù))
- ROUND_HALF_UP:四舍五入(取更近的整數(shù))
- ROUND_HALF_DOWN:跟ROUND_HALF_UP 差別僅在于0.5時會向下取整
- ROUND_HALF_EVEN:取最近的偶數(shù)
- ROUND_UNNECESSARY:不需要取整,如果存在小數(shù)位,就拋ArithmeticException 異常
總結(jié)
到此這篇關(guān)于Java中double精度丟失問題原因及解決辦法的文章就介紹到這了,更多相關(guān)Java double精度丟失問題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用kafka發(fā)送和生產(chǎn)消息的示例
本篇文章主要介紹了Java使用kafka發(fā)送和生產(chǎn)消息的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04Spring使用@Autowired為抽象父類注入依賴代碼實例
這篇文章主要介紹了Spring使用@Autowired為抽象父類注入依賴代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-11-11IDEA新建javaWeb以及Servlet簡單實現(xiàn)小結(jié)
這篇文章主要介紹了IDEA新建javaWeb以及Servlet簡單實現(xiàn)小結(jié),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11SpringBoot 導出數(shù)據(jù)生成excel文件返回方式
這篇文章主要介紹了SpringBoot 導出數(shù)據(jù)生成excel文件返回方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10Java?數(shù)據(jù)結(jié)構(gòu)進階二叉樹題集下
二叉樹可以簡單理解為對于一個節(jié)點來說,最多擁有一個上級節(jié)點,同時最多具備左右兩個下級節(jié)點的數(shù)據(jù)結(jié)構(gòu)。本文將帶你通過實際題目來熟練掌握2022-04-04