淺析Java?BigDecimal為什么可以不丟失精度
在金融領(lǐng)域,為了保證數(shù)據(jù)的精度,往往會使用BigDecimal
。本文就來探討下為什么BigDecimal
可以保證精度不丟失。
類介紹
首先來看一下BigDecimal
的類聲明以及幾個屬性:
public class BigDecimal extends Number implements Comparable<BigDecimal> { // 該BigDecimal的未縮放值 private final BigInteger intVal; // 精度,可以理解成小數(shù)點后的位數(shù) private final int scale; // BigDecimal中的十進制位數(shù),如果位數(shù)未知,則為0(備用信息) private transient int precision; // Used to store the canonical string representation, if computed. // 這個我理解就是存實際的BigDecimal值 private transient String stringCache; // 擴大成long型數(shù)值后的值 private final transient long intCompact; }
從例子入手
通過debug來發(fā)現(xiàn)源碼中的奧秘是了解類運行機制很好的方式。 請看下面的testBigDecimal
方法:
@Test public void testBigDecimal() { BigDecimal bigDecimal1 = BigDecimal.valueOf(2.36); BigDecimal bigDecimal2 = BigDecimal.valueOf(3.5); BigDecimal resDecimal = bigDecimal1.add(bigDecimal2); System.out.println(resDecimal); }
在執(zhí)行了BigDecimal.valueOf(2.36)
后,查看debug信息可以發(fā)現(xiàn)上述提到的幾個屬性被賦了值:
接下來進到add
方法里面,看看它是怎么計算的:
/** * Returns a BigDecimal whose value is (this + augend), * and whose scale is max(this.scale(), augend.scale()). */ public BigDecimal add(BigDecimal augend) { if (this.intCompact != INFLATED) { if ((augend.intCompact != INFLATED)) { return add(this.intCompact, this.scale, augend.intCompact, augend.scale); } else { return add(this.intCompact, this.scale, augend.intVal, augend.scale); } } else { if ((augend.intCompact != INFLATED)) { return add(augend.intCompact, augend.scale, this.intVal, this.scale); } else { return add(this.intVal, this.scale, augend.intVal, augend.scale); } } }
看一下傳進來的值:
進入第8行的add方法:
private static BigDecimal add(final long xs, int scale1, final long ys, int scale2) { long sdiff = (long) scale1 - scale2; if (sdiff == 0) { return add(xs, ys, scale1); } else if (sdiff < 0) { int raise = checkScale(xs,-sdiff); long scaledX = longMultiplyPowerTen(xs, raise); if (scaledX != INFLATED) { return add(scaledX, ys, scale2); } else { BigInteger bigsum = bigMultiplyPowerTen(xs,raise).add(ys); return ((xs^ys)>=0) ? // same sign test new BigDecimal(bigsum, INFLATED, scale2, 0) : valueOf(bigsum, scale2, 0); } } else { int raise = checkScale(ys,sdiff); long scaledY = longMultiplyPowerTen(ys, raise); if (scaledY != INFLATED) { return add(xs, scaledY, scale1); } else { BigInteger bigsum = bigMultiplyPowerTen(ys,raise).add(xs); return ((xs^ys)>=0) ? new BigDecimal(bigsum, INFLATED, scale1, 0) : valueOf(bigsum, scale1, 0); } } }
這個例子中,該方法傳入的參數(shù)分別是:xs=236,scale1=2,ys=35,scale2=1 該方法首先計算scale1 - scale2
,根據(jù)差值走不同的計算邏輯,這里求出來是1,所以進入到最下面的else代碼塊(這塊是關(guān)鍵):
- 首先17行校驗了一下數(shù)值范圍
- 18行將ys擴大了10的n次倍,這里n=raise=1,所以返回的scaledY=350
- 接著就進入到20行的add方法:
private static BigDecimal add(long xs, long ys, int scale){ long sum = add(xs, ys); if (sum!=INFLATED) return BigDecimal.valueOf(sum, scale); return new BigDecimal(BigInteger.valueOf(xs).add(ys), scale); }
這個方法很簡單,就是計算和,然后返回BigDecimal
對象:
結(jié)論
所以可以得出結(jié)論:BigDecimal
在計算時,實際會把數(shù)值擴大10的n次倍,變成一個long
型整數(shù)進行計算,整數(shù)計算時自然可以實現(xiàn)精度不丟失。同時結(jié)合精度scale
,實現(xiàn)最終結(jié)果的計算。
到此這篇關(guān)于淺析Java BigDecimal為什么可以不丟失精度的文章就介紹到這了,更多相關(guān)Java BigDecimal精度丟失內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java調(diào)用setStroke()方法設(shè)置筆畫屬性的語法
這篇文章主要介紹了Java調(diào)用setStroke()方法設(shè)置筆畫屬性的語法,如何改變線條的粗細(xì)、虛實和定義線段端點的形狀、風(fēng)格等,需要的朋友可以參考下2017-09-09JDK1.7以上javaFTP上傳刪除文件的實現(xiàn)方法
下面小編就為大家分享一篇JDK1.7以上javaFTP上傳刪除文件的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-11-11Mybatis分頁插件Pagehelper的PageInfo字段屬性使用及解釋
這篇文章主要介紹了Mybatis分頁插件Pagehelper的PageInfo字段屬性使用及解釋,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05淺談Java開發(fā)架構(gòu)之領(lǐng)域驅(qū)動設(shè)計DDD落地
DDD(Domain-Driven Design 領(lǐng)域驅(qū)動設(shè)計)是由Eric Evans最先提出,目的是對軟件所涉及到的領(lǐng)域進行建模,以應(yīng)對系統(tǒng)規(guī)模過大時引起的軟件復(fù)雜性的問題2021-06-06Spring MVC學(xué)習(xí)之DispatcherServlet請求處理詳析
這篇文章主要給大家介紹了關(guān)于Spring MVC學(xué)習(xí)教程之DispatcherServlet請求處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11