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

詳解java中BigDecimal精度問題

 更新時(shí)間:2021年05月11日 14:35:37   作者:雨點(diǎn)的名字  
這篇文章主要介紹了java BigDecimal精度問題,對(duì)精確計(jì)算感興趣的同學(xué),可以參考下

一、背景

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

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

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

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

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

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í)際控制臺(tái)輸出

為什么會(huì)這樣

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

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

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

二、BigDecimal構(gòu)造函數(shù)

1、四種構(gòu)造函數(shù)

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

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

toString()          //將BigDecimal對(duì)象的數(shù)值轉(zhuǎn)換成字符串。
doubleValue()       //將BigDecimal對(duì)象中的值以雙精度數(shù)返回。
floatValue()        //將BigDecimal對(duì)象中的值以單精度數(shù)返回。
longValue()         //將BigDecimal對(duì)象中的值以長(zhǎng)整數(shù)返回。
intValue()          //將BigDecimal對(duì)象中的值以整數(shù)返回。

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

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);
}

控制臺(tái)實(shí)際輸出

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

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

這個(gè)在new BigDecimal(double)類型的構(gòu)造函數(shù)上的注解有解釋說明。

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

0.1000000000000000055511151231257827021181583404541015625。 這是因?yàn)?.1不能像double (或者作為任何有限長(zhǎng)度的二進(jìn)制分?jǐn)?shù))精確地表示。

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

3、如何解決

有兩種常用的解決辦法。

1、是將double 通過Double.toString(double)先轉(zhuǎn)為String,然后放入BigDecimal的String構(gòu)造函數(shù)中。

2、不通過BigDecimal的構(gòu)造函數(shù),而是通過它的靜態(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);
}

運(yùn)行結(jié)果

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

三、常用方法

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余數(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的絕對(duì)值=" + 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的絕對(duì)值=10.5

4.5的相反數(shù)=-4.5

這里把除法單獨(dú)再講一下,因?yàn)槌ú僮鞯臅r(shí)候會(huì)有除不盡的情況,,比如 3,5/3,這時(shí)會(huì)報(bào)錯(cuò)java.lang.ArithmeticException: Non-terminating decimal expansion;

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

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

2、取舍規(guī)則

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

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

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

 public static void main(String[] args) {

    BigDecimal a = new BigDecimal("1.15");
    BigDecimal b = new BigDecimal("1");

    //不管保留數(shù)字后面是大是小(0除外)都會(huì)進(jìn)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);
    //這個(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);

}

運(yùn)行結(jié)果

divide_1 = 1.2

divide_2 = 1.1

divide_3 = 1.2

divide_4 = 1.1

divide_5 = 1.2

四、格式化

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

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

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

示例

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

運(yùn)行結(jié)果

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

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

1、格式化的時(shí)候沒有指定保留位數(shù)的情況下 默認(rèn)保留2位。

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

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

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

相關(guān)文章

  • springboot?security驗(yàn)證碼的登錄實(shí)例

    springboot?security驗(yàn)證碼的登錄實(shí)例

    這篇文章主要介紹了springboot?security驗(yàn)證碼的登錄實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • SpringMVC中Json數(shù)據(jù)格式轉(zhuǎn)換

    SpringMVC中Json數(shù)據(jù)格式轉(zhuǎn)換

    本文主要介紹了SpringMVC中Json數(shù)據(jù)格式轉(zhuǎn)換的相關(guān)知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來看下吧
    2017-03-03
  • Java如何實(shí)現(xiàn)圖片裁剪預(yù)覽功能

    Java如何實(shí)現(xiàn)圖片裁剪預(yù)覽功能

    通常注冊(cè)賬戶上傳用戶圖像時(shí)需要進(jìn)行預(yù)覽,這篇文章就是教我們?nèi)绾斡?Java 實(shí)現(xiàn)圖片裁剪預(yù)覽功能,需要的朋友可以參考下
    2015-07-07
  • 詳解Java的JDBC中Statement與PreparedStatement對(duì)象

    詳解Java的JDBC中Statement與PreparedStatement對(duì)象

    這篇文章主要介紹了詳解Java的JDBC中Statement與PreparedStatement對(duì)象,PreparedStatement一般來說比使用Statement效率更高,需要的朋友可以參考下
    2015-12-12
  • Java中EnumMap的使用解析

    Java中EnumMap的使用解析

    這篇文章主要介紹了Java中EnumMap的使用解析,EnumMap?是一種特殊的?Map,它要求自身所有的鍵來自某個(gè)枚舉類型,EnumMap?的內(nèi)部可以作為一個(gè)數(shù)組來實(shí)現(xiàn),因此它們的性能非常好,你可以放心地用?EnumMap?來實(shí)現(xiàn)基于枚舉的查詢,需要的朋友可以參考下
    2023-11-11
  • Windows10 Java環(huán)境變量配置過程圖解

    Windows10 Java環(huán)境變量配置過程圖解

    這篇文章主要介紹了Windows10 Java環(huán)境變量配置過程圖解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • java?-jar啟動(dòng)服務(wù)并輸出日志常用命令小結(jié)

    java?-jar啟動(dòng)服務(wù)并輸出日志常用命令小結(jié)

    這篇文章主要介紹了在Linux環(huán)境下運(yùn)行JAR包的幾種方法,包括在命令結(jié)尾添加&使其在后臺(tái)運(yùn)行,使用nohup使程序不掛斷運(yùn)行,以及將日志輸出到指定文件或丟棄,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-03-03
  • Java Kafka分區(qū)發(fā)送及消費(fèi)實(shí)戰(zhàn)

    Java Kafka分區(qū)發(fā)送及消費(fèi)實(shí)戰(zhàn)

    本文主要介紹了Kafka分區(qū)發(fā)送及消費(fèi)實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • springboot整合mybatis實(shí)現(xiàn)數(shù)據(jù)庫的更新批處理方式

    springboot整合mybatis實(shí)現(xiàn)數(shù)據(jù)庫的更新批處理方式

    這篇文章主要介紹了springboot整合mybatis實(shí)現(xiàn)數(shù)據(jù)庫的更新批處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 簡(jiǎn)單聊一聊Java線程池ThreadPoolExecutor

    簡(jiǎn)單聊一聊Java線程池ThreadPoolExecutor

    在使用線程池之后,開啟線程就變成了在線程池當(dāng)中找到一個(gè)空閑的線程,銷毀線程變成了歸還線程到線程池的過程,下面這篇文章主要給大家介紹了關(guān)于Java線程池ThreadPoolExecutor的相關(guān)資料,需要的朋友可以參考下
    2022-06-06

最新評(píng)論

深州市| 桐柏县| 武邑县| 吉林市| 西充县| 彭阳县| 应城市| 宁城县| 龙江县| 苍溪县| 贵南县| 芦溪县| 高台县| 会同县| 治县。| 股票| 夏津县| 和平区| 土默特右旗| 太原市| 高陵县| 博罗县| 永春县| 抚宁县| 乐业县| 三江| 上蔡县| 闽清县| 九寨沟县| 改则县| 抚松县| 合作市| 昂仁县| 南溪县| 昌江| 大理市| 枣庄市| 福州市| 河北省| 澳门| 沽源县|