[Java]保留數值後2位的幾種方法

注:

1、以下分析基於JDK1.8.0_74。

2、實驗以Double數值爲例

 

一、返回double型

(一)Math.round

1、示例

public static void test1() {

   double num = 123.465;

   //double num = 123.4;

   double result = Math.round(num * 100) / 100.0;

   System.out.println("Math.round(),四捨五入:"+result);

   System.out.println();

}

 

2、說明

以上代碼,輸出結果爲:123.47。保留2位小數策略是,先將double類型擴大100倍,然後使用Math.round方法將其四捨五入爲long型整數,最後除以100.0,恰好得到2位小數的double類型值。

但是通過這種方式來保留2位小數並不可靠,畢竟返回的是double類型的數值,如果原double數值小數點後少於2位,如123.4,則最終輸出的結果123.4而不是123.40。

如果num = -123.465,則最終輸出結果爲:-123.46

 

3、源碼分析

(1)入參float類型,返回int值

public static int round(float a) {

   //……

}

水平有限,對JDK1.8之後的內部邏輯不做分析,參考1.7的源碼簡單描述一下。如果入參不等於最接近0.5且比0.5小的浮點數值時,把入參加上0.5後向下取整;否則,返回0。簡而言之,取小於等於(參數+0.5)的最大整數。源碼如下:

public static int round(float a) {

    if (a != 0x1.fffffep-2f) // greatest float value less than 0.5

        return (int)floor(a + 0.5f);

    else

        return 0;

}

 

(2)入參double類型,返回long值

同樣,對JDK1.8之後的內部邏輯不做分析,之前版本的處理邏輯同round(float a)方法一樣,只是入參爲double類型,返回值爲long類型。

 

4、延伸 ―― Math.ceil()、Math.floor()

ceil 天花板;

floor 地板。

顧名思義,一個是向上取整,一個是向下取整。

 

 

(二)BigDecimal.setScale

1、示例

public static void test2() {

   double num = 123.465;

   //double num = 123.4;

   BigDecimal b = new BigDecimal(num);

   double result = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();

   System.out.println("BigDecimal.ROUND_HALF_UP,四捨五入:"+result);

   result = b.setScale(2, BigDecimal.ROUND_HALF_DOWN).doubleValue();

   System.out.println("BigDecimal.ROUND_HALF_DOWN,五舍六入:"+result);

   result = b.setScale(2, BigDecimal.ROUND_UP).doubleValue();

   System.out.println("BigDecimal.ROUND_UP,進位處理(就是直接加1):"+result);

   result = b.setScale(2, BigDecimal.ROUND_DOWN).doubleValue();

   System.out.println("BigDecimal.ROUND_DOWN,直接去掉尾數:"+result);

   System.out.println();

}

 

2、說明

以上代碼分4種策略對double浮點數進行處理,保留2位小數:

BigDecimal.ROUND_HALF_UP表示四捨五入,

BigDecimal.ROUND_HALF_DOWN也是五舍六入,

BigDecimal.ROUND_UP表示進位處理(就是直接加1),

BigDecimal.ROUND_DOWN表示直接去掉尾數。

最終返回double類型浮點數,同Math.round方法一樣,不能保證輸出結果絕對保留2位小數。

注意了,以上提到的ROUND_HALF_UP、ROUND_HALF_DOWN是有可能出現問題的,就比如上面的代碼中num = 123.465,理論上使用ROUND_HALF_DOWN時,最終會輸出123.46,而實際上輸出的是123.47。百思不解,上網查詢瞭解到,有很多童鞋在使用BigDecimal這2個枚舉時都遇到過莫名的精度問題。因此,不推薦使用。

 

 

二、返回String型

(一)DecimalFormat.format

1、示例

public static void test3() {

   double num = 123.465;

   DecimalFormat df = new DecimalFormat("#.00");

   String str = df.format(num);

   System.out.println("DecimalFormat,小數部分四捨五入:"+str);

   System.out.println();

}

 

2、說明

使用DecimalFormat. format保留2位小數,在實例化DecimalFormat時會用到‘0’和‘#’,二者的區別如下:

(1)以‘0’作爲佔位符組成的正則串

整數(或小數)部分的位數比數值的位數多,轉換出的轉換出的結果,不足的位置補0:

new DecimalFormat("0000.00").format(123.46)  //轉換出的結果:0123.46

new DecimalFormat("0.000").format(123.46)  //轉換出的結果: 123.460

new DecimalFormat("0000.000").format(123.46)  //轉換出的結果:0123.460

正則串整數(或小數)部分的位數比數值的位數少,轉換出的轉換出的結果,整數部分不改動,小數部分四捨五入:

new DecimalFormat("0.000").format(123.465)  //轉換出的結果:123.465

new DecimalFormat("00.00").format(123.465)  //轉換出的結果:123.47

new DecimalFormat("0.00").format(123.465)  //轉換出的結果:123.47

(2)以‘#’作爲佔位符組成的正則串

整數(或小數)部分的位數比數值的位數多,轉換出的轉換出的結果不變:

new DecimalFormat("####.##").format(123.46)  //轉換出的結果:123.46

new DecimalFormat("#.###").format(123.46)  //轉換出的結果: 123.46

new DecimalFormat("####.###").format(123.46)  //轉換出的結果:123.46

正則串整數(或小數)部分的位數比數值的位數少,轉換出的轉換出的結果,整數部分不改動,小數部分四捨五入:

new DecimalFormat("#.###").format(123.465)  //轉換出的結果:123.465

new DecimalFormat("##.##").format(123.465)  //轉換出的結果:123.47

new DecimalFormat("#.##").format(123.465)  //轉換出的結果:123.47

 

 

(二)String.format

1、示例

public static void test4() {

   double num = 123.465;

   String format = String.format("%.2f", num);

   System.out.println("String.format,保留後兩位,能四捨五入:"+format);

   System.out.println();

}

 

2、說明

字符串格式化方法 format(),按照JDK中的解釋:使用指定的格式字符串和參數返回格式化的字符串。

其用的佔位符如下:

字母

適用參數類型

說明

%a

浮點數

以16進制輸出浮點數

%b / %B

任意值

如果參數爲 null 則輸出 false,否則輸出 true

%c / %C

字符或整數

輸出對應的 Unicode 字符

%d

整數

對整數進行格式化輸出

%e / %E

浮點數

以科學記數法輸出浮點數

%f

浮點數

對浮點數進行格式化輸出

%g / %G

浮點數

以條件來決定是否以科學記數法方式輸出浮點數

%h / %H

任意值

以 16 進制輸出參數的 hashCode() 返回值

%o

整數

以8進制輸出整數

%s / %S

字符串

對字符串進行格式化輸出

%t

日期時間

對日期時間進行格式化輸出

%x / %X

整數

以16進制輸出整數

%n

換行符

%%

百分號本身

需要注意的是,以上佔位符如果是用的大寫,轉換出來的字母都是大寫;如果是小寫,則轉換出來是原字符串。

 

3、常用示例

(1)顯示部分字符

String.format("%.5s", "Hello, world");       // 輸出 "Hello"

String.format("%.5s...", "Hello, world");    // 輸出 "Hello..."

 

(2)輸出逗號分隔數字

String.format("%,d", 1234567);               // 輸出 "1,234,567"

 

(3)逗號分隔+小數點後2位浮點數

String.format("%,.2f", 1234567.8989);               // 輸出 "1,234,567.90"

 

(4)向左補齊 0 並對齊(僅對數字有效)

String.format("%08d", 123);                // 輸出 "00000123"

 

 

(三)NumberFormat. setMaximumFractionDigits

1、示例

public static void test5() {

   double num = 123.465;

   NumberFormat nf = NumberFormat.getNumberInstance();

   nf.setMaximumFractionDigits(2);

   nf.setRoundingMode(RoundingMode.UP);

   System.out.println("RoundingMode.UP,進位處理(就是直接加1),負數先取絕對值進位處理再負數:"+nf.format(num));

   nf.setRoundingMode(RoundingMode.HALF_UP);

   System.out.println("RoundingMode.HALF_UP,四捨五入,負數先取絕對值再四捨五入再負數:"+nf.format(num));

   nf.setRoundingMode(RoundingMode.DOWN);

   System.out.println("RoundingMode.DOWN,直接去掉尾數,負數先取絕對值再五舍六入再負數:"+nf.format(num));

   nf.setRoundingMode(RoundingMode.HALF_DOWN);

   System.out.println("RoundingMode.HALF_DOWN,五舍六入,負數先取絕對值再五舍六入再負數:"+nf.format(num));

}

 

2、說明

RoundingMode.UP:表示進位處理(就是直接加1),負數先取絕對值進位處理再負數

RoundingMode.HALF_UP:表示四捨五入,負數先取絕對值再五舍六入再負數

RoundingMode.DOWN:表示直接去掉尾數,負數先取絕對值去掉尾數再負數

RoundingMode.HALF_DOWN表示五舍六入,負數先取絕對值再五舍六入再負數

在經過上述試驗後證明,使用RoundingMode.HALF_DOWN並沒有五舍六入而是四捨五入,文檔說明和實際處理結果不一致,這點也許和BigDecimal. ROUND_HALF_DOWN一樣也是JDK本身的bug,因此不深研,不推薦使用。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章