java浮點型精度丟失淺析

java浮點型數(shù)值在運算中會出現(xiàn)精度損失的情況,在業(yè)務(wù)要求比較高比如交易等場景,一般使用BigDecimal來解決精度丟失的情況。最近一個同事在使用BigDecimal時仍然出現(xiàn)了精度損失,簡略記錄一下

測試用例

代碼如下

@Test
    public void fd() {
        double abc = 0.56D;
        System.out.println("abc: " + abc);
        System.out.println("new BigDecimal(abc): " + new BigDecimal(abc));
        System.out.println("BigDecimal.valueOf(abc): " + BigDecimal.valueOf(abc));
    }

輸出

abc: 0.56
new BigDecimal(abc): 0.560000000000000053290705182007513940334320068359375
BigDecimal.valueOf(abc): 0.56

可以看到在使用BigDecimal構(gòu)造器轉(zhuǎn)化浮點型仍然會有損失,而使用valueOf方法則不會出現(xiàn)精度損失。

深入源碼

BigDecimal構(gòu)造器,核心代碼(BigDecimal(double val))如下

public BigDecimal(double val, MathContext mc) {
  .....
    long valBits = Double.doubleToLongBits(val);
    int sign = ((valBits >> 63) == 0 ? 1 : -1);
    int exponent = (int) ((valBits >> 52) & 0x7ffL);
    long significand = (exponent == 0
                      ? (valBits & ((1L << 52) - 1)) << 1
                      : (valBits & ((1L << 52) - 1)) | (1L << 52));
  exponent -= 1075;
  ...
}

劃重點, Double.doubleToLongBits返回根據(jù)IEEE754浮點“雙精度格式”位布局,返回指定浮點值的表示

BigDecimal.valueOf核心代碼

public static BigDecimal valueOf(double val) {
        return new BigDecimal(Double.toString(val));
    }
public BigDecimal(char[] in, int offset, int len, MathContext mc) {
  ....
}

可以看到使用valueOf方法實際上是把double轉(zhuǎn)為String,再調(diào)用string構(gòu)造器的。

那么為什么使用Double.doubleToLongBits會出現(xiàn)精度損失,而使用string構(gòu)造器不會呢。主要原因是BigDecimal使用十進制(BigInteger)+小數(shù)點(scale)位置來表示小數(shù),而不是直接使用二進制,如101.001 = 101001 * 0.1^3,運算時會分成兩部分,BigInteger間的運算以及小數(shù)點位置的更新,這里不再展開。

原理淺析

Double.doubleToLongBits為什么會出現(xiàn)精度損失呢,主要原因是因為浮點型不能用精確的二進制來表述,就如十進制不能準確描述無窮小數(shù)一樣。

浮點型轉(zhuǎn)化為二進制的算法是乘以2直到?jīng)]有了小數(shù)為止,舉個栗子,0.8表示成二進制

0.8*2=1.6 取整數(shù)部分 1

0.6*2=1.2 取整數(shù)部分 1

0.2*2=0.4 取整數(shù)部分 0

0.4*2=0.8 取整數(shù)部分 0

可以看到上述的計算過程出現(xiàn)循環(huán)了,所以說浮點型轉(zhuǎn)化為二進制有時是不可能精確的。

結(jié)論

如果想要把浮點型轉(zhuǎn)化為BigDecimal,盡量選擇使用valueOf方法,而不是使用構(gòu)造器。

本文由 歧途老農(nóng) 創(chuàng)作,采用 CC BY 4.0 CN 協(xié)議 進行許可。 可自由轉(zhuǎn)載、引用,但需署名作者且注明文章出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容