java百分比怎么表示 java百分數是什么數據類型



文章插圖
java百分比怎么表示 java百分數是什么數據類型

文章插圖

BigDecimal
這篇文章我們會介紹一下Java 中的BigDecimal,并且會通過一些例子演示它的用法,例如精度的操作
Java在java.math包中提供的API類BigDecimal,用來對超過16位有效位的數進行精確的運算 。雙精度浮點型變量double可以處理16位有效數,但在實際應用中,可能需要對更大或者更小的數進行運算和處理 。一般情況下,對于那些不需要準確計算精度的數字,我們可以直接使用Float和Double處理,但是Double.valueOf(String) 和Float.valueOf(String)會丟失精度 。所以開發中,如果我們需要精確計算的結果,則必須使用BigDecimal類來操作 。
為什么需要BigDecimal
前面學習基本類型的時候,我們可以使用float 和 double來表示浮點型數字,但是這里有一個問題那就是基本數據類型float 和 double不應該用于高精度數據的表示,例如貨幣,因為浮點類型的float 和 double會丟失數據的精度
double d1 = 374.56;double d2 = 374.26;System.out.println( "d1 - d2 = " + ( d1 - d2 ));這里本應輸出的是0.30,但是實際上輸出如下
d1 - d2 = 0.30000000000001137這就是為什么在金融相關的程序里數據類型如此重要的原因,所以我們更好的選擇是BigDecimal 而不是float 和 double
Java BigDecimal 類
BigDecimal 是不可變的,任意精度的有符號十進制類型的數字,可用于貨幣計算
上面那個例子如果我們是使用BigDecimal來代替double 我們可以獲得準確的值
BigDecimal bd1 = new BigDecimal("374.56");BigDecimal bd2 = new BigDecimal("374.26");System.out.println("bd1 - bd2 = " + bd1.subtract(bd2));現在輸出就和預期的一樣了
bd1 - bd2 = 0.30Java 中的BigDecimal類繼承自Number并且實現了Comparable接口
public class BigDecimal extends Number implements Comparable<BigDecimal> {}BigDecimal 類的構造方法
BigDecimal 提供了很多的構造方法,可以使用int ,char[],BigDecimal,String,doble ,long,int來初始化BigDecimal,BigDecimal 總共提供了18種構造方法,需要注意的實如果使用double 來初始化BigDecimal或許會再次引入精度的問題,下面提供了一個例子
BigDecimal bde = new BigDecimal(23.12);System.out.println("" + bde.toString());輸出結果是這樣的
23.120000000000000994759830064140260219573974609375Thus it is always safe to go with a constructor that takes String as argument when representing a decimal value.
因此使用String 做為構造函數的參數來表示一個十進制的數字的時候總是安全的
BigDecimal bde = new BigDecimal("23.12");System.out.println("" + bde.toString());Output
23.12BigDecimal 的精度
使用BigDecimal的一個理由是BigDecimal提供了精度控制(小數點后的數字的多少)和舍入模式,為了確定小數點后的保留幾位數字你可以使用setScale(int scale) 方法,但是最好的是在使用精度的時候提供舍入模式,也就是setScale的重載方法
setScale(int newScale, int roundingMode)setScale(int newScale, RoundingMode roundingMode)
接下來我們通過一個例子演示一下我們為什么要這樣做,假設我們在通過一個double值構造一個BigDecimal
BigDecimal bde = new BigDecimal(23.12);System.out.println("Value- " + bde.toString());System.out.println("Scaled value- " + bde.setScale(1).toString());Output
Value- 23.120000000000000994759830064140260219573974609375Exception in thread "main" java.lang.ArithmeticException: Rounding necessary at java.base/java.math.BigDecimal.commonNeedIncrement(BigDecimal.java:4495) at java.base/java.math.BigDecimal.needIncrement(BigDecimal.java:4702) at java.base/java.math.BigDecimal.divideAndRound(BigDecimal.java:4677) at java.base/java.math.BigDecimal.setScale(BigDecimal.java:2811) at java.base/java.math.BigDecimal.setScale(BigDecimal.java:2853) at org.netjs.Programs.App.main(App.java:15)從上面的輸出中我們看到進度已經丟失了,輸出的BigDecimal 是23.120000000000000994759830064140260219573974609375
并且我們看到當我們將精度設置為1 的時候并且沒有提供舍入機制的時候導致Arithmetic異常被拋出
BigDecimal 的舍入模式
如果你注意到了上面我們在講精度設置的時候,它其實是有兩個設置精度的重載方法,第二個參數代表的就是舍入模式模式的參數,BigDecimal提供了八種舍入模式,它們通過static final int 進行表示
public final static int ROUND_UP =0;public final static int ROUND_DOWN =1;public final static int ROUND_CEILING =2;public final static int ROUND_FLOOR =3;public final static int ROUND_HALF_UP =4;public final static int ROUND_HALF_DOWN =5;public final static int ROUND_HALF_EVEN =6;public final static int ROUND_UNNECESSARY =7;需要注意的是在java.math包中也提供舍入模式的枚舉值,需要注意的我們是推薦使用枚舉值來代替使用int 類型的常量做舍入摸模式的參數
下面我們在設置進度的同時設置一下舍入模式,來避免Arithmetic異常
@Testpublic void scale() {BigDecimal bde = new BigDecimal(23.12);System.out.println("Scaled value- " + bde.setScale(1,1).toString());}但是我們說了,我們推薦使用枚舉值的舍入模式,而不是直接使用int 類型的常量,接下來我們看一下RoundingMode 提供的枚舉值
CEILING– Rounding mode to round towards positive infinity.DOWN– Rounding mode to round towards zero.FLOOR– Rounding mode to round towards negative infinity.HALF_DOWN– Rounding mode to round towards “nearest neighbor” unless both neighbors are equidistant, in which case round down.HALF_EVEN– Rounding mode to round towards the “nearest neighbor” unless both neighbors are equidistant, in which case, round towards the even neighbor.HALF_UP– Rounding mode to round towards “nearest neighbor” unless both neighbors are equidistant, in which case round up.UNNECESSARY – Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary.UP– Rounding mode to round away from zero.
下面我們通過例子來總結一下這些舍入模式的舍入方式
Result of rounding input to one digit with the given rounding modeInput NumberUPDOWNCEILINGFLOORHALF_UPHALF_DOWNHALF_EVENUNNECESSARY5.56565656throw ArithmeticException2.53232322throw ArithmeticException1.62121222throw ArithmeticException1.12121111throw ArithmeticException1.011111111-1.0-1-1-1-1-1-1-1-1-1.1-2-1-1-2-1-1-1throw ArithmeticException-1.6-2-1-1-2-2-2-2throw ArithmeticException-2.5-3-2-2-3-3-2-2throw ArithmeticException-5.5-6-5-5-6-6-5-6throw ArithmeticException
更多細節參考–docs.oracle.com/javase/8/do…
BigDecimal格式化NumberFormat 格式化
由于NumberFormat類的format()方法可以使用BigDecimal對象作為其參數,可以利用BigDecimal對超出16位有效數字的貨幣值,百分值,以及一般數值進行格式化控制 。
以利用BigDecimal對貨幣和百分比格式化為例 。首先,創建BigDecimal對象,進行BigDecimal的算術運算后,分別建立對貨幣和百分比格式化的引用,最后利用BigDecimal對象作為format()方法的參數,輸出其格式化的貨幣值和百分比 。
@Testpublic void format() {NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立貨幣格式化引用NumberFormat percent = NumberFormat.getPercentInstance();//建立百分比格式化引用percent.setMaximumFractionDigits(3); //百分比小數點最多3位BigDecimal loanAmount = new BigDecimal("15000.48"); //貸款金額BigDecimal interestRate = new BigDecimal("0.008"); //利率BigDecimal interest = loanAmount.multiply(interestRate); //相乘System.out.println("貸款金額:t" + currency.format(loanAmount));System.out.println("利率:t" + percent.format(interestRate));System.out.println("利息:t" + currency.format(interest));}輸出結果
貸款金額:¥15,000.48利率:0.8%利息:¥120.00DecimalFormat 格式化
BigDecimal格式化保留2為小數,不足則補0
@Testpublic void format2(){DecimalFormat df = new DecimalFormat("#.00");System.out.println(formatToNumber(new BigDecimal("3.435")));System.out.println(formatToNumber(new BigDecimal(0)));System.out.println(formatToNumber(new BigDecimal("0.00")));System.out.println(formatToNumber(new BigDecimal("0.001")));System.out.println(formatToNumber(new BigDecimal("0.006")));System.out.println(formatToNumber(new BigDecimal("0.206")));}/** * @desc* 1. 0~1之間的BigDecimal小數,格式化后失去前面的0,則前面直接加上0 。* 2. 傳入的參數等于0,則直接返回字符串"0.00" * 3. 大于1的小數,直接格式化返回字符串 * @return */public String formatToNumber(BigDecimal obj) {DecimalFormat df = new DecimalFormat("#.00");if(obj.compareTo(BigDecimal.ZERO)==0) {return "0.00";}else if(obj.compareTo(BigDecimal.ZERO)>0&&obj.compareTo(new BigDecimal(1))<0){return "0"+df.format(obj).toString();}else {return df.format(obj).toString();}}BigDecimal 例子
最常見的例子就是精度是2(小數點后保留兩位),并且采用四舍五入的舍入模式(如果指定精度的笑一個數字大于等于5則向上取整,否則向下取整)
@Testpublic void examples() {BigDecimal bd1 = new BigDecimal("23.126");System.out.println("bd1 " + bd1.setScale(2, RoundingMode.HALF_UP).toString());}// 輸出結果bd1 23.13【java百分比怎么表示 java百分數是什么數據類型】因為精度設置為2之后,也就是小數點后兩位的后一位數字是6大于等于5,所以向上取整,所以結果是 23.13
BigDecimal bd1 = new BigDecimal("23.1236");System.out.println("bd1 " + bd1.setScale(2, RoundingMode.HALF_UP).toString());因為精度設置為2之后,也就是小數點后兩位的后一位數字是3小于5,所以向下取整,所以結果是 23.12
BigDecimal bd1 = new BigDecimal("-15.567");System.out.println("bd1 " + bd1.setScale(2, RoundingMode.HALF_UP).toString());對于負數,也是同樣的道理,所以輸出是bd1 -15.57
BigDecimal 的特性1. 沒有重載操作符
在Java 中支持的(+, -, *, /)數學運算,BigDecimal并不支持,因為這些操作符是針對基本數據類型的,但是BigDecimal是引用類型,也就是基于對象和類的,因此BigDecimal提供了下面的方法
add,subtract,multiply,and,divide
例如乘法在BigDecimal的實現如下
BigDecimal bd1 = new BigDecimal("15.567");BigDecimal result = BigDecimal.valueOf(68).multiply(bd1);System.out.println("result " + result.toString());Output
result 1058.5562. 使用 compareTo() 來比較BigDecimals 而不是使用 equals()
需要注意如果你使用 equals()來比較兩個BigDecimal數字,那只有當兩個BigDecimal的值和精度都相同的時候equals()猜認為它們是相同的(因此2.0和2.00是不相同的)
BigDecimal bd1 = new BigDecimal("2.00");BigDecimal bd2 = new BigDecimal("2.0");System.out.println("bd1 equals bd2 - " + bd1.equals(bd2));Output
bd1 equals bd2 - false因此你應該使用compareTo()方法來比較兩個BigDecimal 是否是相等的,BigDecimal實現了comparable接口并且提供了自己的compareTo方法,這個方法只會判斷兩個BigDecimal對象的值是否是相等的忽略了兩個數字的精度(ike 2.0 和 2.00 相等的)
對于bd1.compareTo(bd2) 的返回值
-1 bd1 小于 bd2.0 兩個相等的1 bd1 大于 bd2.
BigDecimal bd1 = new BigDecimal("2.00");BigDecimal bd2 = new BigDecimal("2.0");System.out.println("bd1 compareTo bd2 - " + bd1.compareTo(bd2));Output
bd1 compareTo bd2 - 03. BigDecimals 是不可變的
BigDecimal 對象是不可變的,所以是線程安全的,在進行每一次四則運算時,都會產生一個新的對象 ,所以在做加減乘除運算時要記得要保存操作后的值 。
總結當我們在進行有著高精度的計算要求的時候不要使用double和float 因為它們有著精度丟失的問題如果使用BigDecimal的時候,不要選擇double值作為初始化的值,因為它同樣會引入精度的問題如果你使用BigDecimal時候設置了精度,那就同時提供舍入模式,告訴BigDecimal如何舍入從而提供你想要的精度BigDecimal繼承了Number類和實現了Comparable接口BigDecimal 針對加減乘除提供可特定的方法,因為BigDecimal不支持(+, -, *, /)數學運算BigDecimal 的對象是不可變的BigDecimal因為創建對象開銷的原因,所以很多操作都是比原生類型要慢一些的 。