av一区二区在线观看_亚洲男人的天堂网站_日韩亚洲视频_在线成人免费_欧美日韩精品免费观看视频_久草视

您的位置:首頁技術(shù)文章
文章詳情頁

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

瀏覽:2日期:2022-08-24 11:09:55

前言

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

BigDecimal

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

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

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

構(gòu)造BigDecimal實(shí)例

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

@Testpublic 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:

@Testpublic void whenBigDecimalCreatedFromDouble_thenValueMayNotMatch() { BigDecimal bdFromDouble = new BigDecimal(0.1d); assertNotEquals('0.1', bdFromDouble.toString());}

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

@Testpublic 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表示形式。此外,它可以重用對(duì)象實(shí)例。因此,我們應(yīng)該優(yōu)先使用valueOf方法來構(gòu)造函數(shù)。

常用API

方法名 對(duì)應(yīng)方法相關(guān)用法解釋 abs() 絕對(duì)值,scale不變 add(BigDecimal augend) 加,scale為augend和原值scale的較大值 subtract(BigDecimal augend) 減,scale為augend和原值scale的較大值 multiply(BigDecimal multiplicand) 乘,scale為augend和原值scale的和 divide(BigDecimal divisor) 除,原值/divisor,如果不能除盡會(huì)拋出異常,scale與原值一致 divide(BigDecimal divisor, int roundingMode) 除,指定舍入方式,scale與原值一致 divide(BigDecimal divisor, int scale, int roundingMode) 除,指定舍入方式和scale remainder(BigDecimal divisor) 取余,scale與原值一致 divideAndRemainder(BigDecimal divisor) 除法運(yùn)算后返回一個(gè)數(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ù)點(diǎn)左移,scale為原值scale+n movePointRight(int n) 小數(shù)點(diǎn)右移,scale為原值scale+n negate() 取反,scale不變 pow(int n) 冪,原值^n,原值的n次冪 scaleByPowerOfTen(int n) 相當(dāng)于小數(shù)點(diǎn)右移n位,原值*10^n

BigDecimal操作

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

提取屬性

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

@Testpublic void whenGettingAttributes_thenExpectedResult() { BigDecimal bd = new BigDecimal('-12345.6789'); assertEquals(9, bd.precision()); assertEquals(4, bd.scale()); assertEquals(-1, bd.signum());}

比較大小

我們使用compareTo方法比較兩個(gè)BigDecimal的值:

@Testpublic 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ù)位數(shù)那么請(qǐng)使用equals方法:

@Testpublic void whenEqualsCalled_thenSizeAndScaleMatched() { BigDecimal bd1 = new BigDecimal('1.0'); BigDecimal bd2 = new BigDecimal('1.00'); assertFalse(bd1.equals(bd2));}

四則運(yùn)算

BigDecimal 提供了以下四則運(yùn)算的方法:

add ——加法 subtract ——減法 divide ——除法,有可能除不盡,必須顯式聲明保留小數(shù)位數(shù)避免拋出ArithmeticException異常 multiply ——乘法

@Testpublic 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é)運(yùn)算就不得不講四舍五入。比如我們?cè)诮痤~計(jì)算中很容易遇到最終結(jié)算金額為人民幣22.355的情況。因?yàn)樨泿艣]有比分更低的單位所以我們要使用精度和舍入模式規(guī)則對(duì)數(shù)字進(jìn)行剪裁。java提供有兩個(gè)類控制舍入行為RoundingMode和MathContext 。MathContext執(zhí)行的是IEEE 754R標(biāo)準(zhǔn)目前不太明白其使用場(chǎng)景,我們使用的比較多的是枚舉RoundingMode。它提供了八種模式:

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

格式化

數(shù)字格式化可通過操作類java.text.NumberFormat和java.text.DecimalFormat提供的api進(jìn)行操作。其實(shí)我們只需要使用java.text.DecimalFormat,因?yàn)樗砹薔umberFormat。我們來看一下它們的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ù)點(diǎn)位數(shù),不足的位數(shù)以0補(bǔ)位,超出的話按實(shí)際位數(shù)輸出。NumberFormat.setMaximumFractionDigits(int)。設(shè)置最多保留小數(shù)位數(shù),不足不補(bǔ)0。

DecimalFormat

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

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

總結(jié)

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

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 久久精品99久久久久久 | 91精品国产99久久久久久红楼 | 天天操夜 | 精品国产91乱码一区二区三区 | 天堂成人av| 香蕉成人网 | 婷婷av在线 | 美女一级片 | 91人人爽 | 国产免费一级 | 91成人国产 | 日本中文字幕在线播放 | 日韩精品视频在线播放 | 国产自在线 | 国产福利91精品一区二区三区 | 欧美黄色一区二区 | 狠狠干婷婷 | 日韩大片在线观看 | 欧美精品在线视频 | 91久久久精品 | 秋霞av在线 | 亚洲欧美另类在线观看 | 黄色成人在线视频 | 国产1级片| 日韩三级一区 | 国产福利小视频 | www.色中色| 天天爽天天操 | 日本色婷婷| 精品在线播放 | av福利在线 | 少妇一级淫片免费看 | 天天拍天天干 | 欧美日韩毛片 | 天堂视频在线免费观看 | 不卡在线 | 国产一级一片免费播放放a 国产黄色大片 | 精品久久久久久久久久久 | 午夜在线观看视频网站 | 欧美日韩亚洲一区二区三区 | 中文字幕在线观看免费视频 |