2014年3月10日 星期一

【J筆記】Java的小數點進位與四捨五入

在Java中對小數點做四捨五入,是拜歐曾經的痛,不過,只痛那一下下,之後就通體舒暢了。

要在Java中對小數做處理,不得不提到BigDecimal這個類別,使用它的setScale方法,就可以得到想要的小數點處理。

如,要做四捨五入:
package idv.jk.math;

import java.math.BigDecimal;

public class BigDecimalEx {

    public static void main(String... args){

        double a = 3.454;
        double b = new BigDecimal(a)
                           .setScale(1, BigDecimal.ROUND_HALF_UP)
                           .doubleValue();
        System.out.println("四捨五入到小數點下一位: " + b);

        b = new BigDecimal(a)
                        .setScale(2, BigDecimal.ROUND_HALF_UP)
                        .doubleValue();
        System.out.println("四捨五入到小數點下二位: " + b);

    }

}

得到的結果:

四捨五入到小數點下一位: 3.5
四捨五入到小數點下二位: 3.45

setScale中,傳入的第一個參數為要進位的小數點位數,第二個參數表示進位時的方式,如上例就是我們熟知的「四捨五入」,當要捨去的位數為「>= 5」時,就進位,詳細請見Java Doc

大概知道用法後,來看一個令拜歐一度視為「玄學」的現象:
package idv.jk.math;

import java.math.BigDecimal;

public class BigDecimalEx {

    public static void main(String... args){

        double a = 3.45;
        double b = new BigDecimal(a)
                          .setScale(1, BigDecimal.ROUND_HALF_UP)
                          .doubleValue();

        System.out.println("四捨五入到小數點下一位: " + b);       

        b = new BigDecimal(a)
                     .setScale(1, BigDecimal.ROUND_HALF_DOWN)
                     .doubleValue();

        System.out.println("四捨五入到小數點下一位: " + b);

    }

}

在上列程式碼第17行的地方,拜歐將參數改成BigDecimal.ROUND_HALF_DOWN,在Java Doc中,若在setScale使用此參數,則捨去的部位要「> 5」才會做進位,所以「3.45」使用此參數,要進位到小數點下一位,應該是「3.4」,但程式執行得到的結果卻是:

四拾五入到小數點下一位: 3.5
四拾五入到小數點下一位: 3.5

去看了一下BigDecimal中的建構方法中有提到:你宣告一個double變數出來丟進BigDecimal中的建構方法中,如上列的「3.45」,但在Java中,「3.45」真正的值是「3.4500000000000000....555...」,所以上列程式捨去的部份是「> 5」的。

若你要精確地建構「3.45」的BigDecimal物件,就要用new BigDecimal(String str)這個建構方法,如:

package idv.jk.math;

import java.math.BigDecimal;

public class BigDecimalEx {

    public static void main(String... args){

        String a = "3.45";

        double b = new BigDecimal(a)
                           .setScale(1, BigDecimal.ROUND_HALF_UP)
                           .doubleValue();

        System.out.println("四捨五入到小數點下一位: " + b);

        b = new BigDecimal(a)
                     .setScale(1, BigDecimal.ROUND_HALF_DOWN)
                     .doubleValue();

        System.out.println("四捨五入到小數點下一位: " + b);
    }
}

執行結果:

四捨五入到小數點下一位: 3.5
四捨五入到小數點下一位: 3.4