欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何使用BigDecimal實現(xiàn)Java開發(fā)商業(yè)計算

 更新時間:2020年09月21日 10:03:26   作者:碼農(nóng)小胖哥  
這篇文章主要介紹了如何使用BigDecimal實現(xiàn)Java開發(fā)商業(yè)計算,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下

前言

今天群里一個初級開發(fā)者問為什么測試人員測出來他寫的價格計算模塊有計算偏差的問題,他檢查了半天也沒找出問題。這里小胖哥要提醒你,商業(yè)計算請務(wù)必使用BigDecimal,浮點做商業(yè)運算是不精確的。因為計算機無法使用二進制小數(shù)來精確描述我們程序中的十進制小數(shù)。《Effective Java》在第48條也推薦“使用BigDecimal來做精確運算”。今天我們就來總結(jié)歸納其相關(guān)的知識點。

BigDecimal

BigDecimal表示不可變的任意精度帶符號十進制數(shù)。它由兩部分組成:

  • intVal - 未校正精度的整數(shù),類型為BigInteger
  • Scale - 一個32位整數(shù),表示小數(shù)點右邊的位數(shù)

例如,BigDecimal 3.14的未校正值為314,縮放為2。我們使用BigDecimal進行高精度算術(shù)運算。我們還將它用于需要控制比例和舍入行為的計算。如果你的計算是商業(yè)計算請務(wù)必使用計算精確的BigDecimal 。

構(gòu)造BigDecimal實例

我們可以從String,character 數(shù)組,int,long和BigInteger創(chuàng)建一個BigDecimal對象:

@Test
public void theValueMatches() {
  BigDecimal bdFromString = new BigDecimal("0.12");
  BigDecimal bdFromCharArray = new BigDecimal(new char[]{'3', '.', '1', '4', '1', '5'});
  BigDecimal bdlFromInt = new BigDecimal(42);
  BigDecimal bdFromLong = new BigDecimal(123412345678901L);
  BigInteger bigInteger = BigInteger.probablePrime(100, new Random());
  BigDecimal bdFromBigInteger = new BigDecimal(bigInteger);
  assertEquals("0.12", bdFromString.toString());
  assertEquals("3.1415", bdFromCharArray.toString());
  assertEquals("42", bdlFromInt.toString());
  assertEquals("123412345678901", bdFromLong.toString());
  assertEquals(bigInteger.toString(), bdFromBigInteger.toString());
}

我們還可以從double創(chuàng)建BigDecimal:

@Test
public void whenBigDecimalCreatedFromDouble_thenValueMayNotMatch() {
  BigDecimal bdFromDouble = new BigDecimal(0.1d);
  assertNotEquals("0.1", bdFromDouble.toString());
}

我們發(fā)現(xiàn)在這種情況下,結(jié)果與預(yù)期的結(jié)果不同(即0.1)。這是因為:這個轉(zhuǎn)換結(jié)果是double的二進制浮點值的精確十進制表示,其值得結(jié)果不是我們可以預(yù)測的.我們應(yīng)該使用String構(gòu)造函數(shù)而不是double構(gòu)造函數(shù)。另外,我們可以使用valueOf靜態(tài)方法將double轉(zhuǎn)換為BigDecimal 或者直接使用其未校正數(shù)加小數(shù)位數(shù) :

@Test
public void whenBigDecimalCreatedUsingValueOf_thenValueMatches() {
    BigDecimal bdFromDouble = BigDecimal.valueOf(0.1d);
    BigDecimal bigFromLong=BigDecimal.valueOf(1,1);

    assertEquals("0.1", bdFromDouble.toString());
    assertEquals("0.1", bigFromLong.toString());
}

在轉(zhuǎn)換為BigDecimal之前,此方法將double轉(zhuǎn)換為其String表示形式。此外,它可以重用對象實例。因此,我們應(yīng)該優(yōu)先使用valueOf方法來構(gòu)造函數(shù)。

常用API

方法名 對應(yīng)方法相關(guān)用法解釋
abs() 絕對值,scale不變
add(BigDecimal augend) 加,scale為augend和原值scale的較大值
subtract(BigDecimal augend) 減,scale為augend和原值scale的較大值
multiply(BigDecimal multiplicand) 乘,scale為augend和原值scale的和
divide(BigDecimal divisor) 除,原值/divisor,如果不能除盡會拋出異常,scale與原值一致
divide(BigDecimal divisor, int roundingMode) 除,指定舍入方式,scale與原值一致
divide(BigDecimal divisor, int scale, int roundingMode) 除,指定舍入方式和scale
remainder(BigDecimal divisor) 取余,scale與原值一致
divideAndRemainder(BigDecimal divisor) 除法運算后返回一個數(shù)組存放除盡和余數(shù) 如 23/3 返回 {7,2}
divideToIntegralValue(BigDecimal divisor) 除,只保留整數(shù)部分,但scale仍與原值一致
max(BigDecimal val) 較大值,返回原值與val中的較大值,與結(jié)果的scale一致
min(BigDecimal val) 較小值,與結(jié)果的scale一致
movePointLeft(int n) 小數(shù)點左移,scale為原值scale+n
movePointRight(int n) 小數(shù)點右移,scale為原值scale+n
negate() 取反,scale不變
pow(int n) 冪,原值^n,原值的n次冪
scaleByPowerOfTen(int n) 相當(dāng)于小數(shù)點右移n位,原值*10^n

BigDecimal操作

BigDecimal上的操作就像其他Number類(Integer,Long,Double等)一樣,BigDecimal提供算術(shù)和比較操作的操作。它還提供了縮放操作,舍入和格式轉(zhuǎn)換的操作。它不會使算術(shù)運算符(+ - /*)或邏輯運算符(> < | &) 過載。相反,我們使用BigDecimal相應(yīng)的方法 - 加,減,乘,除和比較。并且BigDecimal具有提取各種屬性的方法。

提取屬性

精度,小數(shù)位數(shù)和符號:

@Test
public void whenGettingAttributes_thenExpectedResult() {
  BigDecimal bd = new BigDecimal("-12345.6789");

  assertEquals(9, bd.precision());
  assertEquals(4, bd.scale());
  assertEquals(-1, bd.signum());
}

比較大小

我們使用compareTo方法比較兩個BigDecimal的值:

@Test
public void whenComparingBigDecimals_thenExpectedResult() {
  BigDecimal bd1 = new BigDecimal("1.0");
  BigDecimal bd2 = new BigDecimal("1.00");
  BigDecimal bd3 = new BigDecimal("2.0");

  assertTrue(bd1.compareTo(bd3) < 0);
  assertTrue(bd3.compareTo(bd1) > 0);
  assertTrue(bd1.compareTo(bd2) == 0);
  assertTrue(bd1.compareTo(bd3) <= 0);
  assertTrue(bd1.compareTo(bd2) >= 0);
  assertTrue(bd1.compareTo(bd3) != 0);
}

上面的方法在比較時忽略了小數(shù)位。如果你既要比較精度又要比較小數(shù)位數(shù)那么請使用equals方法:

@Test
public void whenEqualsCalled_thenSizeAndScaleMatched() {
  BigDecimal bd1 = new BigDecimal("1.0");
  BigDecimal bd2 = new BigDecimal("1.00");

  assertFalse(bd1.equals(bd2));
}

四則運算

BigDecimal 提供了以下四則運算的方法:

  • add ——加法
  • subtract ——減法
  • divide ——除法,有可能除不盡,必須顯式聲明保留小數(shù)位數(shù)避免拋出ArithmeticException異常
  • multiply ——乘法
@Test
public void whenPerformingArithmetic_thenExpectedResult() {
  BigDecimal bd1 = new BigDecimal("4.0");
  BigDecimal bd2 = new BigDecimal("2.0");

  BigDecimal sum = bd1.add(bd2);
  BigDecimal difference = bd1.subtract(bd2);
  BigDecimal quotient = bd1.divide(bd2);
  BigDecimal product = bd1.multiply(bd2);

  assertTrue(sum.compareTo(new BigDecimal("6.0")) == 0);
  assertTrue(difference.compareTo(new BigDecimal("2.0")) == 0);
  assertTrue(quotient.compareTo(new BigDecimal("2.0")) == 0);
  assertTrue(product.compareTo(new BigDecimal("8.0")) == 0);
}

四舍五入

既然是數(shù)學(xué)運算就不得不講四舍五入。比如我們在金額計算中很容易遇到最終結(jié)算金額為人民幣22.355的情況。因為貨幣沒有比分更低的單位所以我們要使用精度和舍入模式規(guī)則對數(shù)字進行剪裁。java提供有兩個類控制舍入行為RoundingMode和MathContext 。MathContext執(zhí)行的是IEEE 754R標(biāo)準(zhǔn)目前不太明白其使用場景,我們使用的比較多的是枚舉RoundingMode。它提供了八種模式:

RoundingMode.UP:以小數(shù)位為原點 是正數(shù)取右邊,負(fù)數(shù)取左邊
RoundingMode.DOWN:以小數(shù)位為原點 也就是正數(shù)取左邊,負(fù)數(shù)取右邊
RoundingMode.FLOOR:取左邊最近的正數(shù)
RoundingMode.CEILING:取右邊最近的整數(shù)
RoundingMode.HALF_DOWN:五舍六入,負(fù)數(shù)先取絕對值再五舍六入再負(fù)數(shù)
RoundingMode.HALF_UP:四舍五入,負(fù)數(shù)原理同上
RoundingMode.HALF_EVEN:這個比較繞,整數(shù)位若是奇數(shù)則四舍五入,若是偶數(shù)則五舍六入
RoundingMode.ROUND_UNNECESSARY:不需要取整,如果存在小數(shù)位,就拋ArithmeticException 異常

格式化

數(shù)字格式化可通過操作類java.text.NumberFormat和java.text.DecimalFormat提供的api進行操作。其實我們只需要使用java.text.DecimalFormat,因為它代理了NumberFormat。我們來看一下它們的api:

NumberFormat

NumberFormat.getInstance(Locale)、getNumberInstance(Locale)。返回指定語言環(huán)境的通用數(shù)值格式。
NumberFormat.getCurrencyInstance(Locale)。返回指定語言環(huán)境的貨幣格式。
NumberFormat.getPercentInstance(Locale)。返回指定語言環(huán)境的百分比格式。
NumberFormat.getIntegerInstance(Locale)。返回指定語言環(huán)境的整數(shù)數(shù)值格式。
NumberFormat.setMinimumIntegerDigits(int)。設(shè)置數(shù)的整數(shù)部分所允許的最小位數(shù)。
NumberFormat.setMaximumIntegerDigits(int)。設(shè)置數(shù)的整數(shù)部分所允許的最大位數(shù)。
NumberFormat.setMinimumFractionDigits(int)。設(shè)置最少小數(shù)點位數(shù),不足的位數(shù)以0補位,超出的話按實際位數(shù)輸出。
NumberFormat.setMaximumFractionDigits(int)。設(shè)置最多保留小數(shù)位數(shù),不足不補0。

DecimalFormat

DecimalFormat除了能代理上面的NumberFormat以外,還提供了基于pattern字符串的格式化風(fēng)格,有點類似格式化時間一樣。我們來看看pattern的規(guī)則:

  • “0”——表示一位數(shù)值,如沒有,顯示0。如“0000.0000”,整數(shù)位或小數(shù)位>4,按實際輸出,<4整數(shù)位前面補0小數(shù)位后面補0,湊足4位。
  • “#”——表示任意位數(shù)的整數(shù)。如沒有,則不顯示。在小數(shù)點位使用,只表示一位小數(shù),超出部分四舍五入。如:“#”:無小數(shù),小數(shù)部分四舍五入?!?#”:整數(shù)部分不變,一位小數(shù),四舍五入。“.##”:整數(shù)部分不變,二位小數(shù),四舍五入。
  • “.”——表示小數(shù)點。注意一個pattern中只能出現(xiàn)一次,超過一次將格式化異常。
  • “,”——與模式“0”一起使用,表示逗號。注意一定不能在小數(shù)點后用,否則格式化異常。

總結(jié)

今天對BigDecimal進行了總結(jié)歸納,這篇文章建議你收藏備用,也可以轉(zhuǎn)給其他需要的同學(xué)。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java中反射的學(xué)習(xí)筆記分享

    Java中反射的學(xué)習(xí)筆記分享

    反射是Java編程語言中的一個特性。它允許執(zhí)行的Java程序檢查或?操作?自身,并操作程序的內(nèi)部屬性。本文將通過幾個示例帶大家詳細(xì)了解一下Java中反射的使用,需要的可以參考一下
    2022-11-11
  • Spring?Cache抽象-使用SpEL表達(dá)式解析

    Spring?Cache抽象-使用SpEL表達(dá)式解析

    這篇文章主要介紹了Spring?Cache抽象-使用SpEL表達(dá)式解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • ZooKeeper集群操作及集群Master選舉搭建啟動

    ZooKeeper集群操作及集群Master選舉搭建啟動

    這篇文章主要為大家介紹了ZooKeeper集群操作及集群Master選舉搭的建啟動詳解,<BR>有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Mybatis增刪改查mapper文件寫法詳解

    Mybatis增刪改查mapper文件寫法詳解

    這篇文章主要介紹了Mybatis增刪改查mapper文件寫法的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Java創(chuàng)建多線程服務(wù)器流程

    Java創(chuàng)建多線程服務(wù)器流程

    這篇文章主要介紹了Java創(chuàng)建多線程服務(wù)器流程,以下實例演示了如何使用Socket類的accept()方法和ServerSocket類的MultiThreadServer(socketname)方法來實現(xiàn)多線程服務(wù)器程序
    2023-05-05
  • Spring實戰(zhàn)之Qualifier注解用法示例

    Spring實戰(zhàn)之Qualifier注解用法示例

    這篇文章主要介紹了Spring實戰(zhàn)之Qualifier注解用法,結(jié)合實例形式詳細(xì)分析了spring Qualifier注解相關(guān)配置、定義與使用方法,需要的朋友可以參考下
    2019-12-12
  • Java NIO三大組件與ByteBuffer深入理解及使用

    Java NIO三大組件與ByteBuffer深入理解及使用

    這篇文章主要介紹了Java NIO三大組件與ByteBuffer,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-01-01
  • 深入講解java線程與synchronized關(guān)鍵字

    深入講解java線程與synchronized關(guān)鍵字

    Java 中多線程的同步依靠的是對象鎖機制,synchronized關(guān)鍵字就是利用了封裝對象鎖來實現(xiàn)對共享資源的互斥訪問。下面這篇文章主要介紹了java線程與synchronized關(guān)鍵字的相關(guān)資料,需要的朋友可以參考下。
    2017-03-03
  • Spring boot 實現(xiàn)單個或批量文件上傳功能

    Spring boot 實現(xiàn)單個或批量文件上傳功能

    這篇文章主要介紹了Spring boot 實現(xiàn)單個或批量文件上傳功能,非常不錯,具有一定的參考借鑒價值,需要的朋友參考下吧
    2018-08-08
  • 如何在Spring WebFlux的任何地方獲取Request對象

    如何在Spring WebFlux的任何地方獲取Request對象

    這篇文章主要介紹了如何在Spring WebFlux的任何地方獲取Request對象,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下
    2021-01-01

最新評論