浮點數(double、float)的格式化問題及處理


---恢復內容開始---

 平時常會面臨浮點數的格式處理問題,下面就舉例說一說常見的問題及處理:

    1,科學計數法問題

    一個浮點數123456789.10,在打印的時候變成了1.234567891E8,處理起來很簡單,如:

double d = 123456789.10;
System.out.println(d);//1.234567891E8
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setGroupingUsed(false);
System.out.println(nf.format(d));//打印結果:123456789.10

 

 

    使用NumberFormat的時候要setGroupingUsed(false),否則結果就會變成123,456,789.1。

    再有直接轉為BigDecimal更簡便:

System.out.println(new BigDecimal(d));//打印結果:123456789.10

 

    

    2,指定小數位的位數

    指定浮點數1.010515的小數位的位數:

double d = 1.010515;
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMaximumFractionDigits(10);
System.out.println(nf.format(d));//打印結果:1.010515
nf.setMinimumFractionDigits(10);
System.out.println(nf.format(d));//打印結果:1.0105150000

 

    使用NumberFormat格式化的時候可以設置最大和最小的小數位數,如果要求必須有多少位,就要將最大和最小位數保持一致了。

    再有,轉為BigDecimal也很簡單,會自動補0:

System.out.println(new BigDecimal(d).setScale(10,BigDecimal.ROUND_HALF_UP));//打印結果:1.0105150000

 

    使用BigDecimal的時候要選擇舍入模式,接下來就說說這個問題。

    

    3,四舍五入

    接着看上面的例子,對1.010515進行小數位截取,進行四舍五入:

nf.setMaximumFractionDigits(3);
System.out.println(nf.format(d));//打印結果:1.011 四舍五入
nf.setMaximumFractionDigits(5);
System.out.println(nf.format(d));//打印結果:1.01052 四舍五入

 

    不像BigDecimal,使用NumberFormat指定小數位的時候,不需要指定舍入方式,我們看到結果已經舍入了,但是我們將d+1,然后再看一下:

d = 2.010515;
nf.setMaximumFractionDigits(3);
System.out.println(nf.format(d));//打印結果:2.011 四舍五入
nf.setMaximumFractionDigits(5);
System.out.println(nf.format(d));//打印結果:2.01051 沒有四舍五入

 

    改變d的值后再進行小數位截取,會發現有的時候會四舍五入,有的時候不四舍五入,這時候就會想到指定舍入方式,NumberFormat 有很多舍入模式:UP、DOWN、CEILING、FLOOR、HALF_UP、HALF_DOWN、HALF_EVEN、UNNECESSARY,這些模式其實和BigDecimal的模式是一樣的,NumberFormat的舍入模式就是對BigDecimal的做了一下封裝,並沒有什么不同,比如,RoundingMode.HALF_UP就等於BigDecimal.ROUND_HALF_UP,至於這么多模式我就不再多解釋了,平時使用的時候根據實際的需求選擇一種模式即可,對於我們平時理解的四舍五入對應的模式就是RoundingMode.HALF_UP,我們看代碼:

nf.setRoundingMode(RoundingMode.HALF_UP);
System.out.println(nf.format(d));//打印結果:2.01051 沒有四舍五入

 

    是不是覺得奇怪,為什么沒有四舍五入,換BigDecimal試一下:

System.out.println(new BigDecimal(d).setScale(5,BigDecimal.ROUND_HALF_UP));//打印結果:2.01051 沒有四舍五入

 

    還是沒有四舍五入,再來試試下面的代碼:

System.out.println(nf.format(BigDecimal.valueOf(d)));//打印結果:2.01052 成功了
System.out.println(BigDecimal.valueOf(d).setScale(5,BigDecimal.ROUND_HALF_UP));//打印結果:2.01052 成功了

 

    哎?怎么又都成功了!呵呵,神不神奇,其實這就是下一個問題了(丟失精度)。

 

    4,精度丟失

    還是接着上面的例子說,對2.010515進行四舍五入的時候,只有最后一次的代碼正確的對其進行了四舍五入的格式處理,關鍵在於BigDecimal.valueOf(d),將d轉為了BigDecimal,但是不是隨便轉的:

System.out.println(nf.format(BigDecimal.valueOf(d)));    //打印結果:2.01052 成功了
System.out.println(nf.format(new BigDecimal(d)));        //打印結果:2.01051 失敗
System.out.println(nf.format(new BigDecimal(String.valueOf(d))));    //打印結果:2.01052 又成功了

 

    通過上邊的代碼可知,為了避免精度丟失,盡量將浮點數轉為BigDecimal,並且,要使用BigDecimal.valueOf()函數,如果要用new函數,就要現將浮點數轉為字符串,否則同樣會丟失精度。

    接下來我們還是繼續上邊的例子,我們對d*100進行四舍五入的操作:

nf.setMaximumFractionDigits(3);//保留三位小數
nf.setRoundingMode(RoundingMode.HALF_UP);//四舍五入
System.out.println(nf.format(BigDecimal.valueOf(d*100)));//打印結果:201.051 怎么又失敗了
System.out.println(nf.format(new BigDecimal(String.valueOf(d*100)));//打印結果:201.051 失敗
System.out.println(BigDecimal.valueOf(d*100).setScale(3,BigDecimal.ROUND_HALF_UP));//打印結果:201.051  使用BigDecimal同樣失敗

 

    按照之前說的轉為BigDecimal,但是還是出現錯誤,原因出在2.010515*100上,直接打印一下看看:

System.out.println(d*100);//打印結果:201.05149999999998,不是2.010515

 

    結果精度丟失了,變成了201.05149999999998,所以上面的問題不是四舍五入的模式有bug,而是精度又丟失了,就相當於對201.05149999999998保留三位,四舍五入后就變成了201.051,而不是201.052,怎么解決?還是使用BigDecimal:

System.out.println(nf.format(BigDecimal.valueOf(d).multiply(BigDecimal.valueOf(100))));//打印結果:201.052 成功

 

    通過BigDecimal運算函數來替換運算符,可以保證精度的不丟失,所以成功了,但是並不是所有的情況我們都能轉化,比如d*100是作為double參數傳遞到我們的函數中的,我們無法干預函數之外的行為,怎么辦?

    對於上面的丟失精度問題,可以先判斷一下小數位數,當超多12位的時候認為丟失精度了,先進行一次保留12位小數的四舍五入,然后再進行實際位數的四舍五入(不確定該方法是否通用):

nf.setMaximumFractionDigits(12);
BigDecimal bd = new BigDecimal(nf.format(BigDecimal.valueOf(d*100)));
nf.setMaximumFractionDigits(3);
System.out.println(nf.format(bd));//打印結果:201.052 達到目的

 

    或者使用BigDecimal:

System.out.println(BigDecimal.valueOf(d*100).setScale(12,BigDecimal.ROUND_HALF_UP).setScale(3,BigDecimal.ROUND_HALF_UP));//打印結果:201.052

 

 

    5,正負號問題

    兩個浮點數相減,比如:0.0003-0.0005,取3位小數位:

NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMaximumFractionDigits(3);
nf.setMinimumFractionDigits(nf.getMaximumFractionDigits());
System.out.println(nf.format(0.0003-0.0005));//打印結果:-0.000
System.out.println(BigDecimal.valueOf(0.0003-0.0005).setScale(3,BigDecimal.ROUND_HALF_UP));//0.000

 

    NumberFormat的結果是-0.000,BigDecimal方式的結果是0.000

---恢復內容結束---


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM