double轉換為int以及浮點型相加損失精度問題


最近在做支付相關模塊的業務,數據庫字段卻使用的是double類型,其實也行,只要計算不在sql語句中進行,也是沒有問題的。

預先的類屬性設置的是Double類型,自己算的時候發現小數相加會出現損失精度的情況

如下情形

 @Test public void testDouble(){ Double [] arr = {39.9d,50d,198d,39.9d}; Double verify = 0d; for (Double aDouble : arr) { verify += aDouble ; } System.out.println(verify); }

輸出的結果是:327.79999999999995  理應為:328

待着疑惑試了下js

一看是一個效果,精度都會缺失。百度了下,解釋如下

產生原因:
    計算機並不能識別除了二進制數據以外的任何數據,無論我們使用何種編程語言,在何種編譯環境下工作,都要先把源程序翻譯成二進制的機器碼后才能被計算機識別。
    而在存儲浮點型數據時,會分為三部分進行存儲:
  1.         符號位(Sign): 0代表正,1代表為負
  2.         指數位(Exponent):用於存儲科學計數法中的指數數據,並且采用移位存儲
  3.         尾數部分(Mantissa):尾數部分
    其中前兩位分別為實數符號位和指數符號位,於是存儲結構實際為如下:

 

                    31                  30            29----23        22----0         

               實數符號位    指數符號位    指數位      有效數位

 

       但是,在將十進制浮點數轉換為二進制浮點數時,小數的二進制有時也是不可能精確的,就如同十進制不能准確表示1/3,二進制也無法准確表示1/10,而double類型存儲尾數部分最多只能存儲52位,於是,計算機在存儲該浮點型數據時,便出現了精度丟失。

  例如,11.9的內存存儲大約為:1011. 1110011001100110011001100...

       而在進行浮點類數據計算的時候,浮點參與計算,會左移或右移n位,直到小數點移動到第一個有效數字的右邊。於是11.9在轉化為二進制后 小數點左移3位,就得到1. 011 11100110011001100110(精度丟失2)

       於是最終浮點型運算出現了精度丟失誤差。

 解決方式,java中可以使用 BigDecimal 來解決

 @Test public void test3(){ System.out.println(0.11+2001299.32);//非精確的輸出
        BigDecimal bigDecimal1 = new BigDecimal(Double.toString(0.11)); BigDecimal bigDecimal2 = new BigDecimal(Double.toString(2001299.32)); System.out.println(bigDecimal1.add(bigDecimal2));//精確的輸出
    }

這種方式可以解決,並且很完美,但是還有一種方式比較容易些,畢竟是金額,小數位只有兩位,可以先將其擴大100倍,再進行計算,計算完畢之后再除100,也可以解決(這么不要臉的方式也只有我這么low的程序員使用了),慚愧,出錯了。

在計算的過程總遇到double轉int的情況,總結了下實現的方式

    Double d = 1.7d;

    @Test
    public void test1(){
        // 這樣會報錯,因為double轉換為字符串之后有.
        System.out.println(Integer.parseInt(d.toString()));
    }

    @Test
    public void test2_1(){
        // double 類型可以直接轉為int類型
        double dd = 1.1;
        int ddd = (int)dd;
        System.out.println(ddd);
    }

    @Test
    public void test2_2(){
        // 可以通過強轉進行轉換,Double是包裝類,不能直接進行強轉,可以拆箱之后再次強轉。
        int aa = (int)(Double.parseDouble(d.toString()));
        System.out.println(aa);
    }

    @Test
    public void test3(){
        // 這種方式最為簡單
        System.out.println(d.intValue());
    }

    @Test
    public void test4(){
        DecimalFormat format1 = new DecimalFormat("#");
        String s = format1.format(d);
        System.out.println(s);
    }

    @Test
    public void test5(){
        DecimalFormat format1 = new DecimalFormat("#.#");
        String s = format1.format(1.35);
        System.out.println(s);
    }

    @Test
    public void test6(){
        DecimalFormat format1 = new DecimalFormat("0.000");
        String s = format1.format(1.35);
        System.out.println(s);
    }

 

DecimalFormat 要四舍五入需要加上 setRoundingMode(RoundingMode.HALF_UP); 網上是這么說的,但是自己測試默認就會四舍五入

 

特殊字符說明
“0” 指定位置不存在數字則顯示為0 123.123 ->0000.0000 ->0123.1230
“#” 指定位置不存在數字則不顯示 123.123 -> ####.#### ->123.123
“.” 小數點
“%” 會將結果數字乘以100 后面再加上% 123.123 ->#.00->1.3212%

 嗯,就這么個坑了。

 

 


免責聲明!

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



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