日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

BigDecimal四舍五入与保留位

發(fā)布時間:2023/12/9 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BigDecimal四舍五入与保留位 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1.引言

?

????????借用《Effactive Java》這本書中的話,float和double類型的主要設(shè)計(jì)目標(biāo)是為了科學(xué)計(jì)算和工程計(jì)算。他們執(zhí)行二進(jìn)制浮點(diǎn)運(yùn)算,這是為了在廣域數(shù)值范圍上提供較為精確的快速近似計(jì)算而精心設(shè)計(jì)的。然而,它們沒有提供完全精確的結(jié)果,所以不應(yīng)該被用于要求精確結(jié)果的場合。但是,商業(yè)計(jì)算往往要求結(jié)果精確,這時候BigDecimal就派上大用場啦。

?

?

2.BigDecimal簡介

?

??????? BigDecimal?由任意精度的整數(shù)非標(biāo)度值 和32 位的整數(shù)標(biāo)度 (scale) 組成。如果為零或正數(shù),則標(biāo)度是小數(shù)點(diǎn)后的位數(shù)。如果為負(fù)數(shù),則將該數(shù)的非標(biāo)度值乘以 10 的負(fù)scale 次冪。因此,BigDecimal表示的數(shù)值是(unscaledValue × 10-scale)。

?

?

3.測試代碼

3.1構(gòu)造函數(shù)(主要測試參數(shù)類型為double和String的兩個常用構(gòu)造函數(shù))

?

?????? BigDecimal aDouble =newBigDecimal(1.22);

?

??????? System.out.println("construct witha double value: " + aDouble);

?

??????? BigDecimal aString =newBigDecimal("1.22");

?

???????? System.out.println("constructwith a String value: " + aString);

?

????????你認(rèn)為輸出結(jié)果會是什么呢?如果你沒有認(rèn)為第一個會輸出1.22,那么恭喜你答對了,輸出結(jié)果如下:

?

???????? construct with adoublevalue:1.2199999999999999733546474089962430298328399658203125

?

???????? construct with a String value: 1.22

?

??????? JDK的描述:1、參數(shù)類型為double的構(gòu)造方法的結(jié)果有一定的不可預(yù)知性。有人可能認(rèn)為在Java中寫入newBigDecimal(0.1)所創(chuàng)建的BigDecimal正好等于 0.1(非標(biāo)度值 1,其標(biāo)度為 1),但是它實(shí)際上等于0.1000000000000000055511151231257827021181583404541015625。這是因?yàn)?.1無法準(zhǔn)確地表示為 double(或者說對于該情況,不能表示為任何有限長度的二進(jìn)制小數(shù))。這樣,傳入到構(gòu)造方法的值不會正好等于 0.1(雖然表面上等于該值)。

?

??????? 2、另一方面,String 構(gòu)造方法是完全可預(yù)知的:寫入newBigDecimal("0.1") 將創(chuàng)建一個 BigDecimal,它正好等于預(yù)期的 0.1。因此,比較而言,通常建議優(yōu)先使用String構(gòu)造方法。

?

??????? 3、當(dāng)double必須用作BigDecimal的源時,請注意,此構(gòu)造方法提供了一個準(zhǔn)確轉(zhuǎn)換;它不提供與以下操作相同的結(jié)果:先使用Double.toString(double)方法,然后使用BigDecimal(String)構(gòu)造方法,將double轉(zhuǎn)換為String。要獲取該結(jié)果,請使用static valueOf(double)方法。

3.2?加法操作

?

??????? BigDecimal a =newBigDecimal("1.22");

?

??????? System.out.println("construct witha String value: " + a);

?

??????? BigDecimal b =newBigDecimal("2.22");

?

??????? a.add(b);

?

??????? System.out.println("aplus b is :" + a);

?

????????我們很容易會認(rèn)為會輸出:

?

??????? construct with a Stringvalue: 1.22

?

??????? a plus b is :3.44

?

????????但實(shí)際上a plus b is : 1.22

4.源碼分析

4.1 valueOf(doubleval)方法

?

??? public??static BigDecimal valueOf(double val) {

?

?????? // Reminder: a zero double returns'0.0', so we cannotfastpath

?

?????? // to use the constant ZERO. This mightbe important enough to

?

?????? // justify a factory approach, a cache,or a few private

?

?????? // constants, later.

?

?????? returnnewBigDecimal(Double.toString(val));//見3.1關(guān)于JDK描述的第三點(diǎn)

?

??? }

4.2 add(BigDecimal augend)方法

?

????? public BigDecimal?? add(BigDecimal augend) {

?

????????? long xs =this.intCompact; //整型數(shù)字表示的BigDecimal,例a的intCompact值為122

?

????????? long ys = augend.intCompact;//同上

?

????????? BigInteger fst = (this.intCompact!=INFLATED) ?null :this.intVal;//初始化BigInteger的值,intVal為BigDecimal的一個BigInteger類型的屬性

?

????? ????BigInteger snd =(augend.intCompact!=INFLATED) ?null : augend.intVal;

?

????????? int rscale =this.scale;//小數(shù)位數(shù)

?

?

?

????????? long sdiff = (long)rscale -augend.scale;//小數(shù)位數(shù)之差

?

????????? if (sdiff != 0) {//取小數(shù)位數(shù)多的為結(jié)果的小數(shù)位數(shù)

?

????????????? if (sdiff < 0) {

?

???????????????? int raise =checkScale(-sdiff);

?

???????????????? rscale =augend.scale;

?

???????????????? if (xs ==INFLATED ||

?

???????????????????? (xs=longMultiplyPowerTen(xs,raise)) ==INFLATED)

?

???????????????????? fst=bigMultiplyPowerTen(raise);

?

??????????????? }else {

?

?????????????????? int raise=augend.checkScale(sdiff);

?

?????????????????? if (ys ==INFLATED ||(ys=longMultiplyPowerTen(ys,raise)) ==INFLATED)

?

?????????????????????? snd =augend.bigMultiplyPowerTen(raise);

?

?????????????? }

?

????????? }

?

????????? if (xs !=INFLATED && ys!=INFLATED) {

?

????????????? long sum = xs + ys;

?

????????????? if ( (((sum ^ xs) &(sum ^ys))) >= 0L)//判斷有無溢出

?

???????????????? returnBigDecimal.valueOf(sum,rscale);//返回使用BigDecimal的靜態(tài)工廠方法得到的BigDecimal實(shí)例

?

??????????}

?

?????????? if (fst ==null)

?

?????????????? fst=BigInteger.valueOf(xs);//BigInteger的靜態(tài)工廠方法

?

?????????? if (snd ==null)

?

?????????????? snd =BigInteger.valueOf(ys);

?

?????????? BigInteger sum =fst.add(snd);

?

?????????? return (fst.signum == snd.signum)?new BigDecimal(sum,INFLATED, rscale, 0) :

?

????????????? newBigDecimal(sum,compactValFor(sum),rscale, 0);//返回通過其他構(gòu)造方法得到的BigDecimal對象

?

?????? }

?

?

?

????????以上只是對加法源碼的分析,減乘除其實(shí)最終都返回的是一個新的BigDecimal對象,因?yàn)锽igInteger與BigDecimal都是不可變的(immutable)的,在進(jìn)行每一步運(yùn)算時,都會產(chǎn)生一個新的對象,所以a.add(b);雖然做了加法操作,但是a并沒有保存加操作后的值,正確的用法應(yīng)該是a=a.add(b);

?

?

5.java的四舍五入詳解

?

四舍五入是我們小學(xué)的數(shù)學(xué)問題,這個問題對于我們程序猿來說就類似于1到10的加減乘除那么簡單了。在講解之間我們先看如下一個經(jīng)典的案例:

?

[java] view plaincopy

?

??? public static void main(String[] args){?

??????????? System.out.println("12.5的四舍五入值:" + Math.round(12.5));?

??????????? System.out.println("-12.5的四舍五入值:" + Math.round(-12.5));?

??????? }?

??? Output:?

??? 12.5的四舍五入值:13?

??? -12.5的四舍五入值:-12?

?

??????這是四舍五入的經(jīng)典案例,也是我們參加校招時候經(jīng)常會遇到的(貌似我參加筆試的時候遇到過好多次)。從這兒結(jié)果中我們發(fā)現(xiàn)這兩個絕對值相同的數(shù)字,為何近似值會不同呢?其實(shí)這與Math.round采用的四舍五入規(guī)則來決定。

?

??????四舍五入其實(shí)在金融方面運(yùn)用的非常多,尤其是銀行的利息。我們都知道銀行的盈利渠道主要是利息差,它從儲戶手里收集資金,然后放貸出去,期間產(chǎn)生的利息差就是銀行所獲得的利潤。如果我們采用平常四舍五入的規(guī)則話,這里采用每10筆存款利息計(jì)算作為模型,如下:

?

??????四舍:0.000、0.001、0.002、0.003、0.004。這些舍的都是銀行賺的錢。

?

??????五入:0.005、0.006、0.007、0.008、0.009。這些入的都是銀行虧的錢,分別為:0.005、0.004、.003、0.002、0.001。

?

??????所以對于銀行來說它的盈利應(yīng)該是0.000 + 0.001 + 0.002 + 0.003 + 0.004 - 0.005 - 0.004 - 0.003 -0.002 - 0.001 = -0.005。從結(jié)果中可以看出每10筆的利息銀行可能就會損失0.005元,千萬別小看這個數(shù)字,這對于銀行來說就是一筆非常大的損失。面對這個問題就產(chǎn)生了如下的銀行家涉入法了。該算法是由美國銀行家提出了,主要用于修正采用上面四舍五入規(guī)則而產(chǎn)生的誤差。如下:

?

??????舍去位的數(shù)值小于5時,直接舍去。

?

??????舍去位的數(shù)值大于5時,進(jìn)位后舍去。

?

??????當(dāng)舍去位的數(shù)值等于5時,若5后面還有其他非0數(shù)值,則進(jìn)位后舍去,若5后面是0時,則根據(jù)5前一位數(shù)的奇偶性來判斷,奇數(shù)進(jìn)位,偶數(shù)舍去。

?

??????對于上面的規(guī)則我們舉例說明

?

???????? 11.556 = 11.56 ------六入

?

???????? 11.554 = 11.55 -----四舍

?

???????? 11.5551 = 11.56 -----五后有數(shù)進(jìn)位

?

???????? 11.545 = 11.54 -----五后無數(shù),若前位為偶數(shù)應(yīng)舍去

?

???????? 11.555 = 11.56 -----五后無數(shù),若前位為奇數(shù)應(yīng)進(jìn)位

?

??????下面實(shí)例是使用銀行家舍入法:

?

[java] view plaincopy

?

??? public static void main(String[] args){?

??????????? BigDecimal d = newBigDecimal(100000);????? //存款?

??????????? BigDecimal r = newBigDecimal(0.001875*3);?? //利息?

??????????? BigDecimal i =d.multiply(r).setScale(2,RoundingMode.HALF_EVEN);???? //使用銀行家算法??

?????????????

??????????? System.out.println("季利息是:"+i);?

??????????? }?

??? Output:?

????季利息是:562.50?

?

??????在上面簡單地介紹了銀行家舍入法,目前java支持7中舍入法:

?

??????? 1、ROUND_UP:遠(yuǎn)離零方向舍入。向絕對值最大的方向舍入,只要舍棄位非0即進(jìn)位。

?

??????? 2、ROUND_DOWN:趨向零方向舍入。向絕對值最小的方向輸入,所有的位都要舍棄,不存在進(jìn)位情況。

?

??????? 3、ROUND_CEILING:向正無窮方向舍入。向正最大方向靠攏。若是正數(shù),舍入行為類似于ROUND_UP,若為負(fù)數(shù),舍入行為類似于ROUND_DOWN。Math.round()方法就是使用的此模式。

?

??????? 4、ROUND_FLOOR:向負(fù)無窮方向舍入。向負(fù)無窮方向靠攏。若是正數(shù),舍入行為類似于ROUND_DOWN;若為負(fù)數(shù),舍入行為類似于ROUND_UP。

?

??????? 5、HALF_UP:最近數(shù)字舍入(5進(jìn))。這是我們最經(jīng)典的四舍五入。

?

??????? 6、HALF_DOWN:最近數(shù)字舍入(5舍)。在這里5是要舍棄的。

?

??????? 7、HAIF_EVEN:銀行家舍入法。

?

??????提到四舍五入那么保留位就必不可少了,在java運(yùn)算中我們可以使用多種方式來實(shí)現(xiàn)保留位。

?保留位

?

?????方法一:四舍五入

?

[java] view plaincopy

?

??? double??f?? =?? 111231.5585;?

??? BigDecimal??b?? =?? new??BigDecimal(f);?

??? double??f1?? =?? b.setScale(2,?? RoundingMode.HALF_UP).doubleValue();?

?

??????在這里使用BigDecimal ,并且采用setScale方法來設(shè)置精確度,同時使用RoundingMode.HALF_UP表示使用最近數(shù)字舍入法則來近似計(jì)算。在這里我們可以看出BigDecimal和四舍五入是絕妙的搭配。

?

??????方式二:

[java] view plaincopy

?

??? java.text.DecimalFormat?? df??=new?? java.text.DecimalFormat(”#.00″);?

??? df.format(你要格式化的數(shù)字);?

?

??????例:new java.text.DecimalFormat(”#.00″).format(3.1415926)

?

????? #.00?表示兩位小數(shù) #.0000四位小數(shù) 以此類推…

?

?????方式三:

[java] view plaincopy

?

??? double d = 3.1415926;?

?????

??? String result = String.format(”%.2f”);?

?????

??? %.2f %.?表示 小數(shù)點(diǎn)前任意位數(shù)?? 2 表示兩位小數(shù) 格式后的結(jié)果為f 表示浮點(diǎn)型。?

?

??????方式四:

?

??????此外如果使用struts標(biāo)簽做輸出的話,有個format屬性,設(shè)置為format="0.00"就是保留兩位小數(shù)

?

??????例如:

?

[java] view plaincopy

?

??? <bean:write name="entity"property="dkhAFSumPl"?format="0.00" />?

?????

????或者?

?????

??? <fmt:formatNumbertype="number" value="${10000.22/100}"maxFractionDigits="0"/>?

?????

??? maxFractionDigits表示保留的位數(shù)

?

6.總結(jié)

?

??????? (1)商業(yè)計(jì)算使用BigDecimal。

?

???????(2)盡量使用參數(shù)類型為String的構(gòu)造函數(shù)。

?

??????? (3) BigDecimal都是不可變的(immutable)的,在進(jìn)行每一步運(yùn)算時,都會產(chǎn)生一個新的對象,所以在做加減乘除運(yùn)算時千萬要保存操作后的值。

?

??????? (4)我們往往容易忽略JDK底層的一些實(shí)現(xiàn)細(xì)節(jié),導(dǎo)致出現(xiàn)錯誤,需要多加注意。

?

7.封裝Arith類

[java] view plain copy

?

??? package lj.basic;?

?????

??? import java.math.BigDecimal;?

?????

??? public class Arith?

?????

??? {?

?????

??????? private static final int DEF_DIV_SCALE= 10;?

?????

??????? private Arith()?

?????

??????? {?

?????

??????? }?

?????

??????? /**

???????? *?

??????? ?*?

???????? *?

???????? *?提供精確的加法運(yùn)算。

???????? *?

???????? *?

???????? *?

???????? * @param v1

???????? *????????????被加數(shù)

???????? *?

???????? *?

???????? *?

???????? * @param v2

???????? *????????????加數(shù)

???????? *?

???????? *?

???????? *?

???????? * @return?兩個參數(shù)的和

???????? */?

?????

??????? public static double add(double v1,double v2)?

?????

??????? {?

?????

??????????? BigDecimal b1 = newBigDecimal(Double.toString(v1));?

?????

??????????? BigDecimal b2 = newBigDecimal(Double.toString(v2));?

?????

??????????? returnb1.add(b2).doubleValue();?

?????

??????? }?

?????

??????? /**

???????? *?

???????? *?

???????? *?

???????? *?提供精確的減法運(yùn)算。

???????? *?

???????? *?

???????? *?

???????? * @param v1

???????? *????????????被減數(shù)

???????? *?

???????? *?

???????? *?

???????? * @param v2

???????? *????????????減數(shù)

???????? *?

???????? *?

???????? *?

???????? * @return?兩個參數(shù)的差

???????? */?

?????

??????? public static double sub(double v1,double v2) ?

?????

??????? {?

?????

??????????? BigDecimal b1 = newBigDecimal(Double.toString(v1));?

?????

??????????? BigDecimal b2 = newBigDecimal(Double.toString(v2));?

?????

??????????? returnb1.subtract(b2).doubleValue();?

?????

??????? }?

?????

???????/**

???????? *?

???????? *?

???????? *?

???????? *?提供精確的乘法運(yùn)算。

???????? *?

???????? *?

???????? *?

???????? * @param v1

???????? *????????????被乘數(shù)

???????? *?

???????? *?

???????? *?

???????? * @param v2

???????? *????????????乘數(shù)

???????? *?

???????? *?

???????? *?

???????? * @return?兩個參數(shù)的積

???????? */?

?????

??????? public static double mul(double v1,double v2)?

?????

??????? {?

?????

??????????? BigDecimal b1 = newBigDecimal(Double.toString(v1));?

?????

??????????? BigDecimal b2 = newBigDecimal(Double.toString(v2));?

?????

??????????? returnb1.multiply(b2).doubleValue();?

?????

??????? }?

?????

??????? /**

???????? *?

???????? *?

???????? *?

???????? *?提供(相對)精確的除法運(yùn)算,當(dāng)發(fā)生除不盡的情況時,精確到

???????? *?

???????? *?

???????? *?

???????? *?小數(shù)點(diǎn)以后10位,以后的數(shù)字四舍五入。

???????? *?

???????? *?

???????? *?

???????? * @param v1

???????? *????????????被除數(shù)

???????? *?

???????? *?

???????? *?

???????? * @param v2

???????? *????????????除數(shù)

???????? *?

???????? *?

???????? *?

???????? * @return?兩個參數(shù)的商

???????? */?

?????

??????? public static double div(double v1,double v2)?

?????

??????? {?

?????

??????????? return div(v1, v2,DEF_DIV_SCALE);?

?????

??????? }?

?????

??????? /**

???????? *?

???????? *?

???????? *?

???????? *?提供(相對)精確的除法運(yùn)算。當(dāng)發(fā)生除不盡的情況時,由scale參數(shù)指

???????? *?

???????? *?

???????? *?

???????? *?定精度,以后的數(shù)字四舍五入。

???????? *?

???????? *?

???????? *?

???????? * @param v1

???????? *????????????被除數(shù)

???????? *?

???????? *?

????????*?

???????? * @param v2

???????? *????????????除數(shù)

???????? *?

???????? *?

???????? *?

???????? * @param scale

???????? *????????????表示表示需要精確到小數(shù)點(diǎn)以后幾位。

???????? *?

???????? *?

???????? *?

???????? * @return?兩個參數(shù)的商

???????? */?

?????

??????? public static double div(double v1,double v2, int scale)?

?????

??????? {?

?????

??????????? if (scale < 0)?

?????

??????????? {?

?????

??????????????? throw newIllegalArgumentException(?

??????????????????????? "The scale must bea positive integer or zero");?

?????

??????????? }?

?????

??????????? BigDecimal b1 = newBigDecimal(Double.toString(v1));?

?????

??????????? BigDecimal b2 = newBigDecimal(Double.toString(v2));?

?????

??????????? return b1.divide(b2, scale,BigDecimal.ROUND_HALF_UP).doubleValue();?

?????

??????? }?

?????

??????? /**

???????? *?

???????? *?

???????? *?

???????? *?提供精確的小數(shù)位四舍五入處理。

???????? *?

???????? *?

???????? *?

???????? * @param v

???????? *????????????需要四舍五入的數(shù)字

???????? *?

???????? *?

???????? *?

???????? * @param scale

???????? *????????????小數(shù)點(diǎn)后保留幾位

???????? *?

???????? *?

???????? *?

???????? * @return?四舍五入后的結(jié)果

???????? */?

?????

??????? public static double round(double v,int scale) {?

?????

??????????? if (scale < 0)?

?????

??????????? {?

?????

??????????????? throw newIllegalArgumentException(?

??????????????????????? "The scale must bea positive integer or zero");?

?????

??????????? }?

?????

??????????? BigDecimal b = newBigDecimal(Double.toString(v));?

?????

??????????? BigDecimal one = newBigDecimal("1");?

?????

??????????? return b.divide(one, scale,BigDecimal.ROUND_HALF_UP).doubleValue();?

?????

??????? }?

?????

??? }

文章出自http://blog.csdn.net/liujian928730/article/details/50542534

總結(jié)

以上是生活随笔為你收集整理的BigDecimal四舍五入与保留位的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。